Skip to main content

polyglot_sql/
parser.rs

1//! SQL Parser -- recursive-descent parser that converts a token stream into an AST.
2//!
3//! The central type is [`Parser`], which consumes tokens produced by the
4//! [`Tokenizer`](crate::tokens::Tokenizer) and builds a tree of [`Expression`]
5//! nodes covering the full SQL grammar: queries, DML, DDL, set operations,
6//! window functions, CTEs, and dialect-specific extensions for 30+ databases.
7//!
8//! The simplest entry point is [`Parser::parse_sql`], which tokenizes and
9//! parses a SQL string in one call.
10//!
11//! # Static configuration maps
12//!
13//! This module also exports several `LazyLock<HashSet<TokenType>>` constants
14//! (ported from Python sqlglot's `parser.py`) that classify token types:
15//!
16//! - [`TYPE_TOKENS`] -- all tokens that represent SQL data types
17//! - [`NESTED_TYPE_TOKENS`] -- parametric types like `ARRAY`, `MAP`, `STRUCT`
18//! - [`RESERVED_TOKENS`] -- tokens that cannot be used as unquoted identifiers
19//! - [`NO_PAREN_FUNCTIONS`] / [`NO_PAREN_FUNCTION_NAMES`] -- zero-argument
20//!   functions that may be written without parentheses (e.g. `CURRENT_DATE`)
21//! - [`DB_CREATABLES`] -- object kinds valid after `CREATE` (TABLE, VIEW, etc.)
22//! - [`SUBQUERY_PREDICATES`] -- tokens introducing subquery predicates (ANY, ALL, EXISTS)
23
24use crate::error::{Error, Result};
25use crate::expressions::*;
26use crate::tokens::{Span, Token, TokenType, Tokenizer, TokenizerConfig};
27use std::collections::HashSet;
28use std::sync::LazyLock;
29
30// =============================================================================
31// Parser Configuration Maps (ported from Python SQLGlot parser.py)
32// =============================================================================
33
34/// NO_PAREN_FUNCTIONS: Functions that can be called without parentheses
35/// Maps TokenType to the function name for generation
36/// Python: NO_PAREN_FUNCTIONS = {TokenType.CURRENT_DATE: exp.CurrentDate, ...}
37pub static NO_PAREN_FUNCTIONS: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
38    let mut set = HashSet::new();
39    set.insert(TokenType::CurrentDate);
40    set.insert(TokenType::CurrentDateTime);
41    set.insert(TokenType::CurrentTime);
42    set.insert(TokenType::CurrentTimestamp);
43    set.insert(TokenType::CurrentUser);
44    set.insert(TokenType::CurrentRole);
45    set.insert(TokenType::CurrentSchema);
46    set.insert(TokenType::CurrentCatalog);
47    // Additional no-paren functions (from tokens.rs)
48    set.insert(TokenType::LocalTime);
49    set.insert(TokenType::LocalTimestamp);
50    set.insert(TokenType::SysTimestamp);
51    set.insert(TokenType::UtcDate);
52    set.insert(TokenType::UtcTime);
53    set.insert(TokenType::UtcTimestamp);
54    set.insert(TokenType::SessionUser);
55    set
56});
57
58/// NO_PAREN_FUNCTION_NAMES: String names that can be no-paren functions
59/// These are often tokenized as Var/Identifier instead of specific TokenTypes
60pub static NO_PAREN_FUNCTION_NAMES: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
61    crate::function_registry::NO_PAREN_FUNCTION_NAME_LIST
62        .iter()
63        .copied()
64        .collect()
65});
66
67/// STRUCT_TYPE_TOKENS: Tokens that represent struct-like types
68/// Python: STRUCT_TYPE_TOKENS = {TokenType.FILE, TokenType.NESTED, TokenType.OBJECT, ...}
69pub static STRUCT_TYPE_TOKENS: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
70    let mut set = HashSet::new();
71    set.insert(TokenType::File);
72    set.insert(TokenType::Nested);
73    set.insert(TokenType::Object);
74    set.insert(TokenType::Struct);
75    // Note: UNION is part of STRUCT_TYPE_TOKENS in Python but we handle it as a set operation
76    set
77});
78
79/// NESTED_TYPE_TOKENS: Tokens that can have nested type parameters
80/// Python: NESTED_TYPE_TOKENS = {TokenType.ARRAY, TokenType.LIST, ...}
81pub static NESTED_TYPE_TOKENS: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
82    let mut set = HashSet::new();
83    set.insert(TokenType::Array);
84    set.insert(TokenType::List);
85    set.insert(TokenType::LowCardinality);
86    set.insert(TokenType::Map);
87    set.insert(TokenType::Nullable);
88    set.insert(TokenType::Range);
89    // Include STRUCT_TYPE_TOKENS
90    set.insert(TokenType::File);
91    set.insert(TokenType::Nested);
92    set.insert(TokenType::Object);
93    set.insert(TokenType::Struct);
94    set
95});
96
97/// Check if an uppercased type name is a known SQL custom type that should stay uppercased.
98/// Used to distinguish between known types like DATETIME2, SYSNAME etc. and user-defined types
99/// like UserDefinedTableType that should preserve their original case.
100fn convert_name_is_known_custom(name: &str) -> bool {
101    // Known SQL types that appear in the _ (default) branch of parse_data_type
102    // These should remain uppercased.
103    matches!(
104        name,
105        "DATETIME2"
106            | "DATETIMEOFFSET"
107            | "SMALLDATETIME"
108            | "DATETIME"
109            | "NVARCHAR2"
110            | "VARCHAR2"
111            | "NCHAR"
112            | "MONEY"
113            | "SMALLMONEY"
114            | "TINYINT"
115            | "MEDIUMINT"
116            | "BYTEINT"
117            | "SUPER"
118            | "HLLSKETCH"
119            | "TIMETZ"
120            | "TIMESTAMPTZ"
121            | "SYSNAME"
122            | "XML"
123            | "SQL_VARIANT"
124            | "HIERARCHYID"
125            | "ROWVERSION"
126            | "IMAGE"
127            | "CURSOR"
128            | "TABLE"
129            | "UNIQUEIDENTIFIER"
130            | "VARIANT"
131            | "OBJECT"
132            | "NUMBER"
133            | "BINARY_FLOAT"
134            | "BINARY_DOUBLE"
135            | "CLOB"
136            | "NCLOB"
137            | "RAW"
138            | "LONG"
139            | "MEDIUMTEXT"
140            | "LONGTEXT"
141            | "MEDIUMBLOB"
142            | "LONGBLOB"
143            | "TINYTEXT"
144            | "TINYBLOB"
145            | "INT2"
146            | "INT4"
147            | "INT8"
148            | "FLOAT4"
149            | "FLOAT8"
150            | "SERIAL"
151            | "BIGSERIAL"
152            | "SMALLSERIAL"
153            | "YEAR"
154            | "FIXED"
155            | "SIGNED"
156            | "UNSIGNED"
157            | "ROW"
158            | "BIT"
159            | "BOOLEAN"
160            | "BOOL"
161            | "TEXT"
162            | "STRING"
163            | "NTEXT"
164            | "INT128"
165            | "INT256"
166            | "UINT8"
167            | "UINT16"
168            | "UINT32"
169            | "UINT64"
170            | "UINT128"
171            | "UINT256"
172            | "FLOAT32"
173            | "FLOAT64"
174            | "LOWCARDINALITY"
175            | "NULLABLE"
176            | "IPADDRESS"
177            | "IPV4"
178            | "IPV6"
179            | "AGGREGATEFUNCTION"
180            | "SIMPLEAGGREGATEFUNCTION"
181            | "FIXEDSTRING"
182            | "RING"
183            | "NESTED"
184    )
185}
186
187/// ENUM_TYPE_TOKENS: Tokens that represent enum types
188/// Python: ENUM_TYPE_TOKENS = {TokenType.DYNAMIC, TokenType.ENUM, ...}
189pub static ENUM_TYPE_TOKENS: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
190    let mut set = HashSet::new();
191    set.insert(TokenType::Dynamic);
192    set.insert(TokenType::Enum);
193    set.insert(TokenType::Enum8);
194    set.insert(TokenType::Enum16);
195    set
196});
197
198/// AGGREGATE_TYPE_TOKENS: Tokens for aggregate function types (ClickHouse)
199/// Python: AGGREGATE_TYPE_TOKENS = {TokenType.AGGREGATEFUNCTION, ...}
200pub static AGGREGATE_TYPE_TOKENS: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
201    let mut set = HashSet::new();
202    set.insert(TokenType::AggregateFunction);
203    set.insert(TokenType::SimpleAggregateFunction);
204    set
205});
206
207/// TYPE_TOKENS: All tokens that represent data types
208/// Python: TYPE_TOKENS = {TokenType.BIT, TokenType.BOOLEAN, ...}
209pub static TYPE_TOKENS: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
210    let mut set = HashSet::new();
211    // Basic types
212    set.insert(TokenType::Bit);
213    set.insert(TokenType::Boolean);
214    // Integer types
215    set.insert(TokenType::TinyInt);
216    set.insert(TokenType::UTinyInt);
217    set.insert(TokenType::SmallInt);
218    set.insert(TokenType::USmallInt);
219    set.insert(TokenType::MediumInt);
220    set.insert(TokenType::UMediumInt);
221    set.insert(TokenType::Int);
222    set.insert(TokenType::UInt);
223    set.insert(TokenType::BigInt);
224    set.insert(TokenType::UBigInt);
225    set.insert(TokenType::BigNum);
226    set.insert(TokenType::Int128);
227    set.insert(TokenType::UInt128);
228    set.insert(TokenType::Int256);
229    set.insert(TokenType::UInt256);
230    // Floating point types
231    set.insert(TokenType::Float);
232    set.insert(TokenType::Double);
233    set.insert(TokenType::UDouble);
234    // Decimal types
235    set.insert(TokenType::Decimal);
236    set.insert(TokenType::Decimal32);
237    set.insert(TokenType::Decimal64);
238    set.insert(TokenType::Decimal128);
239    set.insert(TokenType::Decimal256);
240    set.insert(TokenType::DecFloat);
241    set.insert(TokenType::UDecimal);
242    set.insert(TokenType::BigDecimal);
243    // String types
244    set.insert(TokenType::Char);
245    set.insert(TokenType::NChar);
246    set.insert(TokenType::VarChar);
247    set.insert(TokenType::NVarChar);
248    set.insert(TokenType::BpChar);
249    set.insert(TokenType::Text);
250    set.insert(TokenType::MediumText);
251    set.insert(TokenType::LongText);
252    set.insert(TokenType::TinyText);
253    set.insert(TokenType::Name);
254    set.insert(TokenType::FixedString);
255    // Binary types
256    set.insert(TokenType::Binary);
257    set.insert(TokenType::VarBinary);
258    set.insert(TokenType::Blob);
259    set.insert(TokenType::MediumBlob);
260    set.insert(TokenType::LongBlob);
261    set.insert(TokenType::TinyBlob);
262    // Date/time types
263    set.insert(TokenType::Date);
264    set.insert(TokenType::Date32);
265    set.insert(TokenType::Time);
266    set.insert(TokenType::TimeTz);
267    set.insert(TokenType::TimeNs);
268    set.insert(TokenType::Timestamp);
269    set.insert(TokenType::TimestampTz);
270    set.insert(TokenType::TimestampLtz);
271    set.insert(TokenType::TimestampNtz);
272    set.insert(TokenType::TimestampS);
273    set.insert(TokenType::TimestampMs);
274    set.insert(TokenType::TimestampNs);
275    set.insert(TokenType::DateTime);
276    set.insert(TokenType::DateTime2);
277    set.insert(TokenType::DateTime64);
278    set.insert(TokenType::SmallDateTime);
279    set.insert(TokenType::Year);
280    set.insert(TokenType::Interval);
281    // JSON types
282    set.insert(TokenType::Json);
283    set.insert(TokenType::JsonB);
284    // UUID
285    set.insert(TokenType::Uuid);
286    // Spatial types
287    set.insert(TokenType::Geography);
288    set.insert(TokenType::GeographyPoint);
289    set.insert(TokenType::Geometry);
290    set.insert(TokenType::Point);
291    set.insert(TokenType::Ring);
292    set.insert(TokenType::LineString);
293    set.insert(TokenType::MultiLineString);
294    set.insert(TokenType::Polygon);
295    set.insert(TokenType::MultiPolygon);
296    // Range types (PostgreSQL)
297    set.insert(TokenType::Int4Range);
298    set.insert(TokenType::Int4MultiRange);
299    set.insert(TokenType::Int8Range);
300    set.insert(TokenType::Int8MultiRange);
301    set.insert(TokenType::NumRange);
302    set.insert(TokenType::NumMultiRange);
303    set.insert(TokenType::TsRange);
304    set.insert(TokenType::TsMultiRange);
305    set.insert(TokenType::TsTzRange);
306    set.insert(TokenType::TsTzMultiRange);
307    set.insert(TokenType::DateRange);
308    set.insert(TokenType::DateMultiRange);
309    // PostgreSQL special types
310    set.insert(TokenType::HllSketch);
311    set.insert(TokenType::HStore);
312    set.insert(TokenType::Serial);
313    set.insert(TokenType::SmallSerial);
314    set.insert(TokenType::BigSerial);
315    // XML
316    set.insert(TokenType::Xml);
317    // Other special types
318    set.insert(TokenType::Super);
319    set.insert(TokenType::PseudoType);
320    set.insert(TokenType::UserDefined);
321    set.insert(TokenType::Money);
322    set.insert(TokenType::SmallMoney);
323    set.insert(TokenType::RowVersion);
324    set.insert(TokenType::Image);
325    set.insert(TokenType::Variant);
326    set.insert(TokenType::Object);
327    set.insert(TokenType::ObjectIdentifier);
328    set.insert(TokenType::Inet);
329    set.insert(TokenType::IpAddress);
330    set.insert(TokenType::IpPrefix);
331    set.insert(TokenType::Ipv4);
332    set.insert(TokenType::Ipv6);
333    set.insert(TokenType::Unknown);
334    set.insert(TokenType::Null);
335    set.insert(TokenType::TDigest);
336    set.insert(TokenType::Vector);
337    set.insert(TokenType::Void);
338    // Include ENUM_TYPE_TOKENS
339    set.insert(TokenType::Dynamic);
340    set.insert(TokenType::Enum);
341    set.insert(TokenType::Enum8);
342    set.insert(TokenType::Enum16);
343    // Include NESTED_TYPE_TOKENS
344    set.insert(TokenType::Array);
345    set.insert(TokenType::List);
346    set.insert(TokenType::LowCardinality);
347    set.insert(TokenType::Map);
348    set.insert(TokenType::Nullable);
349    set.insert(TokenType::Range);
350    set.insert(TokenType::File);
351    set.insert(TokenType::Nested);
352    set.insert(TokenType::Struct);
353    // Include AGGREGATE_TYPE_TOKENS
354    set.insert(TokenType::AggregateFunction);
355    set.insert(TokenType::SimpleAggregateFunction);
356    set
357});
358
359/// SIGNED_TO_UNSIGNED_TYPE_TOKEN: Maps signed types to unsigned types
360/// Python: SIGNED_TO_UNSIGNED_TYPE_TOKEN = {TokenType.BIGINT: TokenType.UBIGINT, ...}
361pub static SIGNED_TO_UNSIGNED_TYPE_TOKEN: LazyLock<
362    std::collections::HashMap<TokenType, TokenType>,
363> = LazyLock::new(|| {
364    let mut map = std::collections::HashMap::new();
365    map.insert(TokenType::BigInt, TokenType::UBigInt);
366    map.insert(TokenType::Int, TokenType::UInt);
367    map.insert(TokenType::MediumInt, TokenType::UMediumInt);
368    map.insert(TokenType::SmallInt, TokenType::USmallInt);
369    map.insert(TokenType::TinyInt, TokenType::UTinyInt);
370    map.insert(TokenType::Decimal, TokenType::UDecimal);
371    map.insert(TokenType::Double, TokenType::UDouble);
372    map
373});
374
375/// SUBQUERY_PREDICATES: Tokens that introduce subquery predicates
376/// Python: SUBQUERY_PREDICATES = {TokenType.ANY: exp.Any, ...}
377pub static SUBQUERY_PREDICATES: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
378    let mut set = HashSet::new();
379    set.insert(TokenType::Any);
380    set.insert(TokenType::All);
381    set.insert(TokenType::Exists);
382    set.insert(TokenType::Some);
383    set
384});
385
386/// DB_CREATABLES: Object types that can be created with CREATE
387/// Python: DB_CREATABLES = {TokenType.DATABASE, TokenType.SCHEMA, ...}
388pub static DB_CREATABLES: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
389    let mut set = HashSet::new();
390    set.insert(TokenType::Database);
391    set.insert(TokenType::Dictionary);
392    set.insert(TokenType::FileFormat);
393    set.insert(TokenType::Model);
394    set.insert(TokenType::Namespace);
395    set.insert(TokenType::Schema);
396    set.insert(TokenType::SemanticView);
397    set.insert(TokenType::Sequence);
398    set.insert(TokenType::Sink);
399    set.insert(TokenType::Source);
400    set.insert(TokenType::Stage);
401    set.insert(TokenType::StorageIntegration);
402    set.insert(TokenType::Streamlit);
403    set.insert(TokenType::Table);
404    set.insert(TokenType::Tag);
405    set.insert(TokenType::View);
406    set.insert(TokenType::Warehouse);
407    set
408});
409
410/// RESERVED_TOKENS: Tokens that cannot be used as identifiers without quoting
411/// These are typically structural keywords that affect query parsing
412pub static RESERVED_TOKENS: LazyLock<HashSet<TokenType>> = LazyLock::new(|| {
413    let mut set = HashSet::new();
414    // Query structure keywords
415    set.insert(TokenType::Select);
416    set.insert(TokenType::From);
417    set.insert(TokenType::Where);
418    set.insert(TokenType::GroupBy);
419    set.insert(TokenType::OrderBy);
420    set.insert(TokenType::Having);
421    set.insert(TokenType::Limit);
422    set.insert(TokenType::Offset);
423    set.insert(TokenType::Union);
424    set.insert(TokenType::Intersect);
425    set.insert(TokenType::Except);
426    set.insert(TokenType::Join);
427    set.insert(TokenType::On);
428    set.insert(TokenType::With);
429    set.insert(TokenType::Into);
430    set.insert(TokenType::Values);
431    set.insert(TokenType::Set);
432    // DDL keywords
433    set.insert(TokenType::Create);
434    set.insert(TokenType::Drop);
435    set.insert(TokenType::Alter);
436    set.insert(TokenType::Truncate);
437    // DML keywords
438    set.insert(TokenType::Insert);
439    set.insert(TokenType::Update);
440    set.insert(TokenType::Delete);
441    set.insert(TokenType::Merge);
442    // Control flow
443    set.insert(TokenType::Case);
444    set.insert(TokenType::When);
445    set.insert(TokenType::Then);
446    set.insert(TokenType::Else);
447    set.insert(TokenType::End);
448    // Boolean operators
449    set.insert(TokenType::And);
450    set.insert(TokenType::Or);
451    set.insert(TokenType::Not);
452    // Comparison
453    set.insert(TokenType::In);
454    set.insert(TokenType::Is);
455    set.insert(TokenType::Between);
456    set.insert(TokenType::Like);
457    set.insert(TokenType::ILike);
458    set.insert(TokenType::Exists);
459    // Literals
460    set.insert(TokenType::Null);
461    set.insert(TokenType::True);
462    set.insert(TokenType::False);
463    // Punctuation tokens (these are always reserved)
464    set.insert(TokenType::LParen);
465    set.insert(TokenType::RParen);
466    set.insert(TokenType::LBracket);
467    set.insert(TokenType::RBracket);
468    set.insert(TokenType::LBrace);
469    set.insert(TokenType::RBrace);
470    set.insert(TokenType::Comma);
471    set.insert(TokenType::Semicolon);
472    set.insert(TokenType::Star);
473    set.insert(TokenType::Eq);
474    set.insert(TokenType::Neq);
475    set.insert(TokenType::Lt);
476    set.insert(TokenType::Lte);
477    set.insert(TokenType::Gt);
478    set.insert(TokenType::Gte);
479    set
480});
481
482// Note: Function name normalization is handled directly in parse_typed_function
483// by matching all aliases to the same typed expression, following Python SQLGlot's pattern.
484// The generator then outputs dialect-specific names via TRANSFORMS.
485
486/// Recursive-descent SQL parser that converts a token stream into an AST.
487///
488/// The parser consumes a `Vec<Token>` produced by the [`Tokenizer`](crate::tokens::Tokenizer)
489/// and builds a tree of [`Expression`] nodes. It supports the full SQL grammar
490/// including SELECT, DML (INSERT/UPDATE/DELETE/MERGE), DDL (CREATE/ALTER/DROP),
491/// window functions, CTEs, set operations, and 30+ dialect-specific extensions.
492///
493/// # Quick start
494///
495/// For most use cases the static helper [`Parser::parse_sql`] is the simplest entry point:
496///
497/// ```rust,ignore
498/// use polyglot_sql::parser::Parser;
499///
500/// let statements = Parser::parse_sql("SELECT 1; SELECT 2")?;
501/// assert_eq!(statements.len(), 2);
502/// ```
503///
504/// For dialect-aware parsing, use [`Parser::with_config`] or
505/// [`Parser::parse_sql_with_config`].
506pub struct Parser {
507    tokens: Vec<Token>,
508    current: usize,
509    config: ParserConfig,
510    /// Original source SQL (used for preserving exact text in Command expressions)
511    source: Option<String>,
512    /// Comments captured by parse_comparison when no comparison operator follows.
513    /// These are leading comments from the first token of an expression that need
514    /// to be placed by the caller (e.g., after an alias, or after an AND operand).
515    pending_leading_comments: Vec<String>,
516}
517
518/// Configuration for the SQL [`Parser`].
519///
520/// Controls dialect-specific parsing behavior. Most users can rely on the
521/// `Default` implementation; set `dialect` when you need to handle syntax
522/// that is unique to a particular database engine (e.g. BigQuery backtick
523/// quoting, TSQL square-bracket identifiers, Snowflake QUALIFY clause).
524#[derive(Debug, Clone, Default)]
525pub struct ParserConfig {
526    /// Allow trailing commas in SELECT lists (e.g. BigQuery permits `SELECT a, b, FROM t`).
527    pub allow_trailing_commas: bool,
528    /// Dialect type for dialect-specific parsing behavior.
529    pub dialect: Option<crate::dialects::DialectType>,
530}
531
532impl Parser {
533    /// Create a new parser from a pre-tokenized token stream with default configuration.
534    ///
535    /// Prefer [`Parser::parse_sql`] if you are starting from a raw SQL string.
536    pub fn new(tokens: Vec<Token>) -> Self {
537        Self {
538            tokens,
539            current: 0,
540            config: ParserConfig::default(),
541            source: None,
542            pending_leading_comments: Vec::new(),
543        }
544    }
545
546    /// Create a parser from a pre-tokenized token stream with a custom [`ParserConfig`].
547    pub fn with_config(tokens: Vec<Token>, config: ParserConfig) -> Self {
548        Self {
549            tokens,
550            current: 0,
551            config,
552            source: None,
553            pending_leading_comments: Vec::new(),
554        }
555    }
556
557    /// Create a parser with source SQL attached.
558    ///
559    /// The original SQL text is stored so that `Command` expressions (unparsed
560    /// dialect-specific statements) can preserve the exact source verbatim.
561    pub fn with_source(tokens: Vec<Token>, config: ParserConfig, source: String) -> Self {
562        Self {
563            tokens,
564            current: 0,
565            config,
566            source: Some(source),
567            pending_leading_comments: Vec::new(),
568        }
569    }
570
571    /// Parse one or more SQL statements from a raw string.
572    ///
573    /// This is the main entry point for most callers. It tokenizes the input with
574    /// the default [`TokenizerConfig`], then parses all semicolon-separated
575    /// statements and returns them as a `Vec<Expression>`.
576    ///
577    /// # Errors
578    ///
579    /// Returns an error if the input contains invalid tokens or syntax that the
580    /// parser cannot recognize.
581    ///
582    /// # Example
583    ///
584    /// ```rust,ignore
585    /// let stmts = Parser::parse_sql("SELECT a FROM t WHERE x = 1")?;
586    /// ```
587    pub fn parse_sql(sql: &str) -> Result<Vec<Expression>> {
588        let tokenizer = Tokenizer::default();
589        let tokens = tokenizer.tokenize(sql)?;
590        let mut parser = Parser::with_source(tokens, ParserConfig::default(), sql.to_string());
591        parser.parse()
592    }
593
594    /// Parse SQL from a string using a custom [`TokenizerConfig`].
595    ///
596    /// Use this variant when the source dialect requires non-default tokenizer
597    /// settings (e.g. different string quoting or comment syntax).
598    pub fn parse_sql_with_config(
599        sql: &str,
600        tokenizer_config: TokenizerConfig,
601    ) -> Result<Vec<Expression>> {
602        let tokenizer = Tokenizer::new(tokenizer_config);
603        let tokens = tokenizer.tokenize(sql)?;
604        let mut parser = Parser::with_source(tokens, ParserConfig::default(), sql.to_string());
605        parser.parse()
606    }
607
608    /// Parse all remaining statements from the token stream.
609    ///
610    /// Consumes tokens until the end of input, splitting on semicolons.
611    /// Returns one `Expression` per statement.
612    pub fn parse(&mut self) -> Result<Vec<Expression>> {
613        let mut statements = Vec::new();
614
615        while !self.is_at_end() {
616            let mut stmt = self.parse_statement()?;
617
618            // Before consuming the semicolon, capture its leading comments
619            // and attach them to the statement (e.g., SELECT foo\n/* comment */\n;)
620            if self.check(TokenType::Semicolon) {
621                let semi_comments = self.current_leading_comments().to_vec();
622                if !semi_comments.is_empty() {
623                    stmt = Expression::Annotated(Box::new(Annotated {
624                        this: stmt,
625                        trailing_comments: semi_comments,
626                    }));
627                }
628            }
629
630            // ClickHouse: consume trailing SETTINGS key=val, ... after any statement
631            if matches!(
632                self.config.dialect,
633                Some(crate::dialects::DialectType::ClickHouse)
634            ) && self.check(TokenType::Settings)
635            {
636                self.skip(); // consume SETTINGS
637                let _ = self.parse_settings_property()?;
638            }
639
640            // ClickHouse: consume trailing FORMAT <name> after any statement
641            if matches!(
642                self.config.dialect,
643                Some(crate::dialects::DialectType::ClickHouse)
644            ) && self.check(TokenType::Format)
645            {
646                self.skip(); // consume FORMAT
647                             // Accept any identifier/keyword/Null as format name
648                if self.check(TokenType::Null) {
649                    self.skip();
650                } else if self.is_identifier_token() || self.check_keyword() {
651                    self.skip();
652                }
653            }
654
655            // ClickHouse: PARALLEL WITH between statements (multi-statement execution)
656            if matches!(
657                self.config.dialect,
658                Some(crate::dialects::DialectType::ClickHouse)
659            ) && self.check_identifier("PARALLEL")
660                && self.check_next(TokenType::With)
661            {
662                self.skip(); // consume PARALLEL
663                self.skip(); // consume WITH
664                statements.push(stmt);
665                continue;
666            }
667
668            // After parsing a statement, the next token must be a semicolon or EOF.
669            // If not, there are unconsumed tokens which indicates a parse error.
670            // This matches Python sqlglot's behavior (parser.py line 1826-1827).
671            if !self.is_at_end() && !self.check(TokenType::Semicolon) {
672                if matches!(
673                    self.config.dialect,
674                    Some(crate::dialects::DialectType::ClickHouse)
675                ) {
676                    // ClickHouse fallback: consume unconsumed tokens until semicolon/EOF.
677                    // This matches Python sqlglot's _parse_as_command behavior for
678                    // ClickHouse-specific syntax that we don't fully parse yet.
679                    while !self.is_at_end() && !self.check(TokenType::Semicolon) {
680                        self.skip();
681                    }
682                } else {
683                    return Err(self.parse_error("Invalid expression / Unexpected token"));
684                }
685            }
686
687            // Consume optional semicolons (ClickHouse allows multiple like `;;`)
688            while self.match_token(TokenType::Semicolon) {}
689
690            statements.push(stmt);
691        }
692
693        Ok(statements)
694    }
695
696    /// Parse a single SQL statement from the current position in the token stream.
697    ///
698    /// Dispatches to the appropriate sub-parser based on the leading keyword
699    /// (SELECT, INSERT, CREATE, etc.). Unknown or dialect-specific statements
700    /// fall through to a `Command` expression that preserves the raw SQL text.
701    pub fn parse_statement(&mut self) -> Result<Expression> {
702        // Skip any leading semicolons
703        while self.match_token(TokenType::Semicolon) {}
704
705        if self.is_at_end() {
706            return Err(self.parse_error("Unexpected end of input"));
707        }
708
709        match self.peek().token_type {
710            // Handle hint comment /*+ ... */ before a statement - convert to regular comment
711            TokenType::Hint => {
712                let hint_token = self.advance();
713                let hint_text = hint_token.text.clone();
714                // Convert hint to regular comment (preserve the + as part of the content)
715                let comment = format!("/* + {} */", hint_text.trim());
716
717                // Parse the following statement
718                let mut stmt = self.parse_statement()?;
719
720                // Attach the comment to the statement's leading_comments
721                match &mut stmt {
722                    Expression::Select(select) => {
723                        select.leading_comments.insert(0, comment);
724                    }
725                    Expression::Insert(insert) => {
726                        insert.leading_comments.insert(0, comment);
727                    }
728                    Expression::Update(update) => {
729                        update.leading_comments.insert(0, comment);
730                    }
731                    Expression::Delete(delete) => {
732                        delete.leading_comments.insert(0, comment);
733                    }
734                    Expression::CreateTable(ct) => {
735                        ct.leading_comments.insert(0, comment);
736                    }
737                    _ => {
738                        // For other statement types, we can't attach comments
739                        // but at least the statement parses successfully
740                    }
741                }
742                Ok(stmt)
743            }
744            TokenType::Select => self.parse_select(),
745            TokenType::With => self.parse_with(),
746            TokenType::Insert => self.parse_insert(),
747            TokenType::Replace => self.parse_replace(),
748            TokenType::Update => self.parse_update(),
749            TokenType::Delete => self.parse_delete(),
750            TokenType::Create => self.parse_create(),
751            TokenType::Drop => self.parse_drop(),
752            TokenType::Alter => self.parse_alter(),
753            TokenType::Truncate => {
754                // TRUNCATE could be TRUNCATE TABLE (statement) or TRUNCATE(a, b) (function)
755                // Check if followed by ( to determine which
756                if self.check_next(TokenType::LParen) {
757                    // TRUNCATE(a, b) - function call
758                    self.parse_expression()
759                } else {
760                    self.parse_truncate()
761                }
762            }
763            TokenType::Values => {
764                // VALUES could be VALUES(...) statement or VALUES 1, 2, 3 (bare values)
765                if self.check_next(TokenType::LParen)
766                    || self.check_next(TokenType::Number)
767                    || self.check_next(TokenType::String)
768                {
769                    self.parse_values()
770                } else {
771                    // "values" by itself is an identifier/expression
772                    self.parse_expression()
773                }
774            }
775            TokenType::Use => self.parse_use(),
776            TokenType::Cache => self.parse_cache(),
777            TokenType::Uncache => self.parse_uncache(),
778            TokenType::Refresh => {
779                self.skip(); // consume REFRESH
780                self.parse_refresh()?
781                    .ok_or_else(|| self.parse_error("Failed to parse REFRESH statement"))
782            }
783            TokenType::Load => self.parse_load_data(),
784            TokenType::Grant => self.parse_grant(),
785            TokenType::Revoke => self.parse_revoke(),
786            TokenType::Comment => self.parse_comment(),
787            TokenType::Merge => {
788                self.skip(); // consume MERGE
789                self.parse_merge()?
790                    .ok_or_else(|| self.parse_error("Failed to parse MERGE statement"))
791            }
792            TokenType::Set => self.parse_set(),
793            TokenType::Database
794                if matches!(
795                    self.config.dialect,
796                    Some(crate::dialects::DialectType::Teradata)
797                ) =>
798            {
799                // Teradata: DATABASE tduser -> USE tduser
800                self.skip(); // consume DATABASE
801                let name = self.expect_identifier_or_keyword()?;
802                Ok(Expression::Use(Box::new(Use {
803                    kind: None,
804                    this: Identifier::new(name),
805                })))
806            }
807            TokenType::Lock
808                if matches!(
809                    self.config.dialect,
810                    Some(crate::dialects::DialectType::Teradata)
811                ) =>
812            {
813                self.parse_locking_statement()
814            }
815            TokenType::Command => {
816                self.skip(); // consume command keyword
817                self.parse_command()?
818                    .ok_or_else(|| self.parse_error("Failed to parse COMMAND statement"))
819            }
820            TokenType::Rename
821                if matches!(
822                    self.config.dialect,
823                    Some(crate::dialects::DialectType::Teradata)
824                        | Some(crate::dialects::DialectType::ClickHouse)
825                ) =>
826            {
827                self.skip(); // consume RENAME
828                self.parse_command()?
829                    .ok_or_else(|| self.parse_error("Failed to parse RENAME statement"))
830            }
831            TokenType::Pragma => self.parse_pragma(),
832            TokenType::Rollback => self.parse_rollback(),
833            TokenType::Commit => self.parse_commit(),
834            TokenType::Begin => self.parse_transaction(),
835            TokenType::End => {
836                // In PostgreSQL, END is an alias for COMMIT (END [WORK|TRANSACTION])
837                // In TSQL and other dialects, END is a block delimiter (BEGIN...END)
838                if matches!(
839                    self.config.dialect,
840                    Some(crate::dialects::DialectType::PostgreSQL)
841                ) {
842                    self.parse_end_transaction()
843                } else {
844                    self.skip(); // consume END
845                    Ok(Expression::Command(Box::new(Command {
846                        this: "END".to_string(),
847                    })))
848                }
849            }
850            TokenType::Start => self.parse_start_transaction(),
851            TokenType::Describe | TokenType::Desc => self.parse_describe(),
852            TokenType::Show => self.parse_show(),
853            TokenType::Copy => self.parse_copy(),
854            TokenType::Put => self.parse_put(),
855            TokenType::Kill
856                if matches!(
857                    self.config.dialect,
858                    Some(crate::dialects::DialectType::ClickHouse)
859                ) =>
860            {
861                self.skip(); // consume KILL
862                self.parse_command()?
863                    .ok_or_else(|| self.parse_error("Failed to parse KILL statement"))
864            }
865            TokenType::Kill => self.parse_kill(),
866            TokenType::Execute => {
867                // ClickHouse: EXECUTE AS username statement → parse as command
868                if matches!(
869                    self.config.dialect,
870                    Some(crate::dialects::DialectType::ClickHouse)
871                ) {
872                    self.skip(); // consume EXECUTE
873                    self.parse_command()?
874                        .ok_or_else(|| self.parse_error("Failed to parse EXECUTE statement"))
875                } else {
876                    self.parse_execute()
877                }
878            }
879            TokenType::Declare => {
880                self.skip(); // consume DECLARE
881                self.parse_declare()?
882                    .ok_or_else(|| self.parse_error("Failed to parse DECLARE statement"))
883            }
884            // GET is a command only when followed by @ (stage reference), otherwise it's a function
885            // If followed by ( it should be parsed as GET() function, so fall through to expression parsing
886            TokenType::Get
887                if self.check_next(TokenType::DAt) || !self.check_next(TokenType::LParen) =>
888            {
889                self.parse_get_command()
890            }
891            TokenType::Var
892                if self.peek().text.eq_ignore_ascii_case("RM")
893                    || self.peek().text.eq_ignore_ascii_case("REMOVE") =>
894            {
895                self.parse_rm_command()
896            }
897            TokenType::Var if self.peek().text.eq_ignore_ascii_case("CALL") => self.parse_call(),
898            TokenType::Var
899                if self.peek().text.eq_ignore_ascii_case("EXCHANGE")
900                    && matches!(
901                        self.config.dialect,
902                        Some(crate::dialects::DialectType::ClickHouse)
903                    ) =>
904            {
905                self.skip(); // consume EXCHANGE
906                self.parse_command()?
907                    .ok_or_else(|| self.parse_error("Failed to parse EXCHANGE statement"))
908            }
909            // EXPLAIN is treated as DESCRIBE (MySQL maps EXPLAIN -> DESCRIBE)
910            TokenType::Var if self.peek().text.eq_ignore_ascii_case("EXPLAIN") => {
911                self.parse_describe()
912            }
913            // LOCK TABLES / UNLOCK TABLES (MySQL)
914            TokenType::Var
915                if self.peek().text.eq_ignore_ascii_case("LOCK")
916                    || self.peek().text.eq_ignore_ascii_case("UNLOCK") =>
917            {
918                self.skip(); // consume LOCK/UNLOCK
919                self.parse_command()?
920                    .ok_or_else(|| self.parse_error("Failed to parse LOCK/UNLOCK statement"))
921            }
922            TokenType::Var if self.peek().text.eq_ignore_ascii_case("ANALYZE") => {
923                self.skip(); // consume ANALYZE
924                self.parse_analyze()?
925                    .ok_or_else(|| self.parse_error("Failed to parse ANALYZE statement"))
926            }
927            // TSQL: PRINT expression
928            TokenType::Var if self.peek().text.eq_ignore_ascii_case("PRINT") => {
929                self.skip(); // consume PRINT
930                self.parse_command()?
931                    .ok_or_else(|| self.parse_error("Failed to parse PRINT statement"))
932            }
933            // TSQL: WAITFOR DELAY '00:00:05' / WAITFOR TIME '23:00:00'
934            TokenType::Var if self.peek().text.eq_ignore_ascii_case("WAITFOR") => {
935                self.skip(); // consume WAITFOR
936                self.parse_command()?
937                    .ok_or_else(|| self.parse_error("Failed to parse WAITFOR statement"))
938            }
939            // TSQL: BULK INSERT table FROM 'file' WITH (options)
940            TokenType::Var if self.peek().text.eq_ignore_ascii_case("BULK") => {
941                self.skip(); // consume BULK
942                self.parse_command()?
943                    .ok_or_else(|| self.parse_error("Failed to parse BULK INSERT statement"))
944            }
945            // ClickHouse: CHECK TABLE t [PARTITION p] [SETTINGS ...]
946            TokenType::Check
947                if matches!(
948                    self.config.dialect,
949                    Some(crate::dialects::DialectType::ClickHouse)
950                ) =>
951            {
952                self.skip(); // consume CHECK
953                self.parse_command()?
954                    .ok_or_else(|| self.parse_error("Failed to parse CHECK statement"))
955            }
956            // ClickHouse: SETTINGS key=value, ... (standalone statement or after another statement)
957            TokenType::Settings
958                if matches!(
959                    self.config.dialect,
960                    Some(crate::dialects::DialectType::ClickHouse)
961                ) =>
962            {
963                self.skip(); // consume SETTINGS
964                self.parse_command()?
965                    .ok_or_else(|| self.parse_error("Failed to parse SETTINGS statement"))
966            }
967            // ClickHouse: SYSTEM STOP/START MERGES, etc.
968            TokenType::System
969                if matches!(
970                    self.config.dialect,
971                    Some(crate::dialects::DialectType::ClickHouse)
972                ) =>
973            {
974                self.skip(); // consume SYSTEM
975                self.parse_command()?
976                    .ok_or_else(|| self.parse_error("Failed to parse SYSTEM statement"))
977            }
978            // ClickHouse: RENAME TABLE db.t1 TO db.t2 [, db.t3 TO db.t4 ...]
979            TokenType::Var
980                if self.peek().text.eq_ignore_ascii_case("RENAME")
981                    && matches!(
982                        self.config.dialect,
983                        Some(crate::dialects::DialectType::ClickHouse)
984                    ) =>
985            {
986                self.skip(); // consume RENAME
987                self.parse_command()?
988                    .ok_or_else(|| self.parse_error("Failed to parse RENAME statement"))
989            }
990            // ClickHouse: OPTIMIZE TABLE t [FINAL] [DEDUPLICATE [BY ...]]
991            // MySQL: OPTIMIZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE t1 [, t2, ...]
992            TokenType::Var
993                if self.peek().text.eq_ignore_ascii_case("OPTIMIZE")
994                    && matches!(
995                        self.config.dialect,
996                        Some(crate::dialects::DialectType::ClickHouse)
997                            | Some(crate::dialects::DialectType::MySQL)
998                            | Some(crate::dialects::DialectType::SingleStore)
999                            | Some(crate::dialects::DialectType::Doris)
1000                            | Some(crate::dialects::DialectType::StarRocks)
1001                    ) =>
1002            {
1003                self.skip(); // consume OPTIMIZE
1004                self.parse_command()?
1005                    .ok_or_else(|| self.parse_error("Failed to parse OPTIMIZE statement"))
1006            }
1007            // ClickHouse: EXISTS [TEMPORARY] TABLE/DATABASE/DICTIONARY ...
1008            TokenType::Exists
1009                if matches!(
1010                    self.config.dialect,
1011                    Some(crate::dialects::DialectType::ClickHouse)
1012                ) && !self.check_next(TokenType::LParen) =>
1013            {
1014                self.skip(); // consume EXISTS
1015                self.parse_command()?
1016                    .ok_or_else(|| self.parse_error("Failed to parse EXISTS statement"))
1017            }
1018            // ClickHouse: SHOW ... (various SHOW commands beyond what's already handled)
1019            TokenType::Var
1020                if self.peek().text.eq_ignore_ascii_case("EXISTS")
1021                    && matches!(
1022                        self.config.dialect,
1023                        Some(crate::dialects::DialectType::ClickHouse)
1024                    ) =>
1025            {
1026                self.skip(); // consume EXISTS
1027                self.parse_command()?
1028                    .ok_or_else(|| self.parse_error("Failed to parse EXISTS statement"))
1029            }
1030            // DuckDB: ATTACH [DATABASE] [IF NOT EXISTS] 'path' [AS alias] [(options)]
1031            TokenType::Var if self.peek().text.eq_ignore_ascii_case("ATTACH") => {
1032                self.skip(); // consume ATTACH
1033                if matches!(
1034                    self.config.dialect,
1035                    Some(crate::dialects::DialectType::ClickHouse)
1036                ) {
1037                    self.parse_command()?
1038                        .ok_or_else(|| self.parse_error("Failed to parse ATTACH statement"))
1039                } else {
1040                    self.parse_attach_detach(true)
1041                }
1042            }
1043            // UNDROP TABLE/SCHEMA/DATABASE (ClickHouse, Snowflake)
1044            TokenType::Var
1045                if self.peek().text.eq_ignore_ascii_case("UNDROP")
1046                    && matches!(
1047                        self.config.dialect,
1048                        Some(crate::dialects::DialectType::ClickHouse)
1049                            | Some(crate::dialects::DialectType::Snowflake)
1050                    ) =>
1051            {
1052                self.skip(); // consume UNDROP
1053                self.parse_command()?
1054                    .ok_or_else(|| self.parse_error("Failed to parse UNDROP statement"))
1055            }
1056            // ClickHouse: DETACH TABLE [IF EXISTS] ... [ON CLUSTER ...]
1057            TokenType::Var
1058                if self.peek().text.eq_ignore_ascii_case("DETACH")
1059                    && matches!(
1060                        self.config.dialect,
1061                        Some(crate::dialects::DialectType::ClickHouse)
1062                    ) =>
1063            {
1064                self.skip(); // consume DETACH
1065                self.parse_command()?
1066                    .ok_or_else(|| self.parse_error("Failed to parse DETACH statement"))
1067            }
1068            // DuckDB: DETACH [DATABASE] [IF EXISTS] name
1069            TokenType::Var if self.peek().text.eq_ignore_ascii_case("DETACH") => {
1070                self.skip(); // consume DETACH
1071                self.parse_attach_detach(false)
1072            }
1073            // DuckDB: INSTALL extension [FROM source]
1074            TokenType::Var if self.peek().text.eq_ignore_ascii_case("INSTALL") => {
1075                self.skip(); // consume INSTALL
1076                self.parse_install(false)
1077            }
1078            // DuckDB: FORCE INSTALL extension | FORCE CHECKPOINT db
1079            TokenType::Var if self.peek().text.eq_ignore_ascii_case("FORCE") => {
1080                self.skip(); // consume FORCE
1081                self.parse_force_statement()
1082            }
1083            // DuckDB: SUMMARIZE [TABLE] expression
1084            TokenType::Var if self.peek().text.eq_ignore_ascii_case("SUMMARIZE") => {
1085                self.skip(); // consume SUMMARIZE
1086                self.parse_summarize_statement()
1087            }
1088            // DuckDB: RESET [SESSION|GLOBAL|LOCAL] variable
1089            TokenType::Var if self.peek().text.eq_ignore_ascii_case("RESET") => {
1090                self.skip(); // consume RESET
1091                self.parse_as_command()?
1092                    .ok_or_else(|| self.parse_error("Failed to parse RESET statement"))
1093            }
1094            // DuckDB statement-level PIVOT/UNPIVOT/PIVOT_WIDER syntax
1095            TokenType::Pivot => {
1096                self.skip(); // consume PIVOT
1097                self.parse_simplified_pivot(false)?
1098                    .ok_or_else(|| self.parse_error("Failed to parse PIVOT statement"))
1099            }
1100            TokenType::Unpivot => {
1101                self.skip(); // consume UNPIVOT
1102                self.parse_simplified_pivot(true)?
1103                    .ok_or_else(|| self.parse_error("Failed to parse UNPIVOT statement"))
1104            }
1105            // DuckDB: PIVOT_WIDER is an alias for PIVOT
1106            TokenType::Var if self.peek().text.eq_ignore_ascii_case("PIVOT_WIDER") => {
1107                self.skip(); // consume PIVOT_WIDER
1108                self.parse_simplified_pivot(false)?
1109                    .ok_or_else(|| self.parse_error("Failed to parse PIVOT_WIDER statement"))
1110            }
1111            // BigQuery procedural FOR...IN...DO loop
1112            TokenType::For => {
1113                self.skip(); // consume FOR
1114                self.parse_for_in()
1115            }
1116            // BigQuery/procedural LOOP, REPEAT, WHILE control flow statements
1117            TokenType::Var if self.peek().text.eq_ignore_ascii_case("LOOP") => {
1118                self.skip(); // consume LOOP
1119                self.parse_command()?
1120                    .ok_or_else(|| self.parse_error("Failed to parse LOOP statement"))
1121            }
1122            TokenType::Var if self.peek().text.eq_ignore_ascii_case("REPEAT") => {
1123                self.skip(); // consume REPEAT
1124                self.parse_command()?
1125                    .ok_or_else(|| self.parse_error("Failed to parse REPEAT statement"))
1126            }
1127            TokenType::Var if self.peek().text.eq_ignore_ascii_case("WHILE") => {
1128                self.skip(); // consume WHILE
1129                self.parse_command()?
1130                    .ok_or_else(|| self.parse_error("Failed to parse WHILE statement"))
1131            }
1132            // Athena/Presto: UNLOAD (SELECT ...) TO 'location' WITH (options)
1133            TokenType::Var if self.peek().text.eq_ignore_ascii_case("UNLOAD") => {
1134                self.parse_unload()
1135            }
1136            // Athena: USING EXTERNAL FUNCTION ... SELECT ...
1137            TokenType::Using => self.parse_using_external_function(),
1138            // BigQuery: EXPORT DATA [WITH CONNECTION conn] OPTIONS (...) AS SELECT ...
1139            TokenType::Var if self.peek().text.eq_ignore_ascii_case("EXPORT") => {
1140                self.parse_export_data()
1141            }
1142            // Presto/Trino: DEALLOCATE PREPARE <name>
1143            TokenType::Var if self.peek().text.eq_ignore_ascii_case("DEALLOCATE") => {
1144                self.parse_deallocate_prepare()
1145            }
1146            // DuckDB FROM-first syntax: FROM tbl = SELECT * FROM tbl
1147            TokenType::From => self.parse_from_first_query(),
1148            TokenType::LParen => {
1149                // Check if this is a parenthesized query (SELECT, WITH, PIVOT, UNPIVOT, FROM, or EXPLAIN inside)
1150                // by looking ahead after the opening paren
1151                let next_is_explain = self.current + 1 < self.tokens.len()
1152                    && self.tokens[self.current + 1].token_type == TokenType::Var
1153                    && self.tokens[self.current + 1]
1154                        .text
1155                        .eq_ignore_ascii_case("EXPLAIN");
1156                if self.check_next(TokenType::Select)
1157                    || self.check_next(TokenType::With)
1158                    || self.check_next(TokenType::Pivot)
1159                    || self.check_next(TokenType::Unpivot)
1160                    || self.check_next(TokenType::From)
1161                    || next_is_explain
1162                {
1163                    // Parse parenthesized query: (SELECT ...) ORDER BY x LIMIT y OFFSET z
1164                    self.skip(); // consume (
1165                    let inner = self.parse_statement()?;
1166                    self.expect(TokenType::RParen)?;
1167                    // Wrap in Subquery to preserve parentheses when used in set operations
1168                    let subquery = Expression::Subquery(Box::new(Subquery {
1169                        this: inner,
1170                        alias: None,
1171                        column_aliases: Vec::new(),
1172                        order_by: None,
1173                        limit: None,
1174                        offset: None,
1175                        distribute_by: None,
1176                        sort_by: None,
1177                        cluster_by: None,
1178                        lateral: false,
1179                        modifiers_inside: false,
1180                        trailing_comments: Vec::new(),
1181                        inferred_type: None,
1182                    }));
1183                    // Check for set operations after the parenthesized query
1184                    let result = self.parse_set_operation(subquery)?;
1185                    // Check for ORDER BY, LIMIT, OFFSET after parenthesized subquery
1186                    self.parse_query_modifiers(result)
1187                } else if self.check_next(TokenType::LParen) {
1188                    // Nested parentheses - could be ((SELECT...)) or ((a, b))
1189                    // For deeply nested queries like (((SELECT 1) UNION SELECT 1) UNION SELECT 1),
1190                    // recurse into parse_statement to handle the inner parenthesized query with set ops
1191                    self.skip(); // consume (
1192                    let inner = self.parse_statement()?;
1193                    // Check for set operations inside the outer parens
1194                    let result = self.parse_set_operation(inner)?;
1195                    self.expect(TokenType::RParen)?;
1196                    let subquery = Expression::Subquery(Box::new(Subquery {
1197                        this: result,
1198                        alias: None,
1199                        column_aliases: Vec::new(),
1200                        order_by: None,
1201                        limit: None,
1202                        offset: None,
1203                        distribute_by: None,
1204                        sort_by: None,
1205                        cluster_by: None,
1206                        lateral: false,
1207                        modifiers_inside: false,
1208                        trailing_comments: Vec::new(),
1209                        inferred_type: None,
1210                    }));
1211                    // Check for set operations after the outer parenthesized query
1212                    let result = self.parse_set_operation(subquery)?;
1213                    let pre_alias_comments = self.previous_trailing_comments().to_vec();
1214                    if self.match_token(TokenType::As) {
1215                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
1216                        let trailing_comments = self.previous_trailing_comments().to_vec();
1217                        Ok(Expression::Alias(Box::new(Alias {
1218                            this: result,
1219                            alias,
1220                            column_aliases: Vec::new(),
1221                            pre_alias_comments,
1222                            trailing_comments,
1223                            inferred_type: None,
1224                        })))
1225                    } else {
1226                        // Check for LIMIT/OFFSET after parenthesized expression
1227                        // e.g., ((SELECT 1)) LIMIT 1
1228                        self.parse_query_modifiers(result)
1229                    }
1230                } else {
1231                    // Regular parenthesized expression like (a, b) or (x)
1232                    // Let parse_expression handle it
1233                    let expr = self.parse_expression()?;
1234                    let pre_alias_comments = self.previous_trailing_comments().to_vec();
1235                    if self.match_token(TokenType::As) {
1236                        // Check for tuple alias: AS ("a", "b", ...)
1237                        if self.match_token(TokenType::LParen) {
1238                            let mut column_aliases = Vec::new();
1239                            loop {
1240                                let col_alias = self.expect_identifier_or_keyword_with_quoted()?;
1241                                column_aliases.push(col_alias);
1242                                if !self.match_token(TokenType::Comma) {
1243                                    break;
1244                                }
1245                            }
1246                            self.expect(TokenType::RParen)?;
1247                            let trailing_comments = self.previous_trailing_comments().to_vec();
1248                            Ok(Expression::Alias(Box::new(Alias {
1249                                this: expr,
1250                                alias: Identifier::empty(),
1251                                column_aliases,
1252                                pre_alias_comments,
1253                                trailing_comments,
1254                                inferred_type: None,
1255                            })))
1256                        } else {
1257                            let alias = self.expect_identifier_or_keyword_with_quoted()?;
1258                            let trailing_comments = self.previous_trailing_comments().to_vec();
1259                            Ok(Expression::Alias(Box::new(Alias {
1260                                this: expr,
1261                                alias,
1262                                column_aliases: Vec::new(),
1263                                pre_alias_comments,
1264                                trailing_comments,
1265                                inferred_type: None,
1266                            })))
1267                        }
1268                    } else {
1269                        Ok(expr)
1270                    }
1271                }
1272            }
1273            _ => {
1274                // Capture leading comments from the first token before parsing
1275                let leading_comments = self.current_leading_comments().to_vec();
1276                // Parse expression and check for optional alias
1277                let expr = self.parse_expression()?;
1278                // Capture any comments between expression and AS keyword
1279                let pre_alias_comments = self.previous_trailing_comments().to_vec();
1280                if self.match_token(TokenType::As) {
1281                    // Capture comments from AS token (e.g., AS /* foo */ (a, b, c))
1282                    // These go into trailing_comments (after the alias), not pre_alias_comments
1283                    let as_comments = self.previous_trailing_comments().to_vec();
1284                    // Check for tuple alias: AS ("a", "b", ...)
1285                    if self.match_token(TokenType::LParen) {
1286                        let mut column_aliases = Vec::new();
1287                        loop {
1288                            let col_alias = self.expect_identifier_or_keyword_with_quoted()?;
1289                            column_aliases.push(col_alias);
1290                            if !self.match_token(TokenType::Comma) {
1291                                break;
1292                            }
1293                        }
1294                        self.expect(TokenType::RParen)?;
1295                        let mut trailing_comments = as_comments;
1296                        trailing_comments.extend_from_slice(self.previous_trailing_comments());
1297                        Ok(Expression::Alias(Box::new(Alias {
1298                            this: expr,
1299                            alias: Identifier::empty(),
1300                            column_aliases,
1301                            pre_alias_comments,
1302                            trailing_comments,
1303                            inferred_type: None,
1304                        })))
1305                    } else {
1306                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
1307                        let mut trailing_comments = self.previous_trailing_comments().to_vec();
1308                        // If there were leading comments on the expression (from a separate line),
1309                        // add them as trailing comments after the alias
1310                        trailing_comments.extend(leading_comments.iter().cloned());
1311                        Ok(Expression::Alias(Box::new(Alias {
1312                            this: expr,
1313                            alias,
1314                            column_aliases: Vec::new(),
1315                            pre_alias_comments,
1316                            trailing_comments,
1317                            inferred_type: None,
1318                        })))
1319                    }
1320                } else if (self.check(TokenType::Var) && !self.check_keyword())
1321                    || self.is_command_keyword_as_alias()
1322                {
1323                    // Implicit alias (without AS) - e.g., "1. x" or "1.x" -> "1. AS x"
1324                    // This handles cases like PostgreSQL's "1.x" which parses as float 1. with alias x
1325                    let alias_text = self.advance().text.clone();
1326                    let trailing_comments = self.previous_trailing_comments().to_vec();
1327                    Ok(Expression::Alias(Box::new(Alias {
1328                        this: expr,
1329                        alias: Identifier::new(alias_text),
1330                        column_aliases: Vec::new(),
1331                        pre_alias_comments,
1332                        trailing_comments,
1333                        inferred_type: None,
1334                    })))
1335                } else if !pre_alias_comments.is_empty() {
1336                    // Wrap in Annotated to preserve trailing comments for expressions without aliases
1337                    match &expr {
1338                        Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
1339                            Ok(Expression::Annotated(Box::new(
1340                                crate::expressions::Annotated {
1341                                    this: expr,
1342                                    trailing_comments: pre_alias_comments,
1343                                },
1344                            )))
1345                        }
1346                        // For expressions that already have trailing_comments fields, don't double-wrap
1347                        _ => Ok(expr),
1348                    }
1349                } else if !leading_comments.is_empty() {
1350                    // Wrap in Annotated to preserve leading comments as trailing comments
1351                    // This matches Python sqlglot which converts leading line comments to trailing block comments
1352                    Ok(Expression::Annotated(Box::new(
1353                        crate::expressions::Annotated {
1354                            this: expr,
1355                            trailing_comments: leading_comments,
1356                        },
1357                    )))
1358                } else {
1359                    Ok(expr)
1360                }
1361            }
1362        }
1363    }
1364
1365    /// Parse a SELECT statement
1366    fn parse_select(&mut self) -> Result<Expression> {
1367        // Capture the SELECT token to get its comments
1368        let select_token = self.expect(TokenType::Select)?;
1369        let leading_comments = select_token.comments;
1370        let post_select_comments = select_token.trailing_comments;
1371
1372        // Parse query hint /*+ ... */ if present (comes immediately after SELECT)
1373        let hint = if self.check(TokenType::Hint) {
1374            Some(self.parse_hint()?)
1375        } else {
1376            None
1377        };
1378
1379        // Parse TOP clause (SQL Server style - comes before DISTINCT)
1380        // But not if TOP is followed by DOT (e.g., SELECT top.x - top is a table alias)
1381        let top = if self.check(TokenType::Top)
1382            && !self.check_next(TokenType::Dot)
1383            && self.match_token(TokenType::Top)
1384        {
1385            // TOP can have parentheses: TOP (10) or without: TOP 10
1386            let (amount, parenthesized) = if self.match_token(TokenType::LParen) {
1387                let expr = self.parse_expression()?;
1388                self.expect(TokenType::RParen)?;
1389                (expr, true)
1390            } else {
1391                (self.parse_primary()?, false)
1392            };
1393            let percent = self.match_token(TokenType::Percent);
1394            let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1395            Some(Top {
1396                this: amount,
1397                percent,
1398                with_ties,
1399                parenthesized,
1400            })
1401        } else {
1402            None
1403        };
1404
1405        // Parse DISTINCT / DISTINCT ON / DISTINCTROW / ALL
1406        // Oracle: UNIQUE is equivalent to DISTINCT (SELECT UNIQUE ... is old-style Oracle syntax)
1407        let is_distinct_token = self.match_token(TokenType::Distinct)
1408            || (matches!(
1409                self.config.dialect,
1410                Some(crate::dialects::DialectType::Oracle)
1411            ) && self.match_token(TokenType::Unique));
1412        let (distinct, distinct_on) = if is_distinct_token {
1413            if self.match_token(TokenType::On) {
1414                // DISTINCT ON (expr, ...)
1415                self.expect(TokenType::LParen)?;
1416                let exprs = self.parse_expression_list()?;
1417                self.expect(TokenType::RParen)?;
1418                (true, Some(exprs))
1419            } else {
1420                (true, None)
1421            }
1422        } else if self.check_identifier("DISTINCTROW") {
1423            // MySQL DISTINCTROW - equivalent to DISTINCT
1424            self.skip();
1425            (true, None)
1426        } else {
1427            // Only consume ALL if it's the SELECT ALL modifier, not if it's a column reference like "all.count"
1428            if self.check(TokenType::All) && !self.check_next(TokenType::Dot) {
1429                self.skip();
1430            }
1431            (false, None)
1432        };
1433
1434        // TSQL: SELECT DISTINCT TOP n - TOP can come after DISTINCT
1435        // If no TOP was parsed before DISTINCT, check for TOP after DISTINCT
1436        let top = if top.is_none()
1437            && self.check(TokenType::Top)
1438            && !self.check_next(TokenType::Dot)
1439            && self.match_token(TokenType::Top)
1440        {
1441            let (amount, parenthesized) = if self.match_token(TokenType::LParen) {
1442                let expr = self.parse_expression()?;
1443                self.expect(TokenType::RParen)?;
1444                (expr, true)
1445            } else {
1446                (self.parse_primary()?, false)
1447            };
1448            let percent = self.match_token(TokenType::Percent);
1449            let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1450            Some(Top {
1451                this: amount,
1452                percent,
1453                with_ties,
1454                parenthesized,
1455            })
1456        } else {
1457            top
1458        };
1459
1460        // Parse MySQL operation modifiers (HIGH_PRIORITY, STRAIGHT_JOIN, SQL_CALC_FOUND_ROWS, etc.)
1461        // These appear after DISTINCT/ALL and before the projections
1462        // Only apply for MySQL-family dialects - other dialects treat these as identifiers
1463        let mut operation_modifiers = Vec::new();
1464        let is_mysql_dialect = matches!(
1465            self.config.dialect,
1466            Some(crate::dialects::DialectType::MySQL)
1467                | Some(crate::dialects::DialectType::SingleStore)
1468                | Some(crate::dialects::DialectType::StarRocks)
1469                | Some(crate::dialects::DialectType::TiDB)
1470                | Some(crate::dialects::DialectType::Doris)
1471        );
1472        if is_mysql_dialect {
1473            const MYSQL_MODIFIERS: &[&str] = &[
1474                "HIGH_PRIORITY",
1475                "STRAIGHT_JOIN",
1476                "SQL_SMALL_RESULT",
1477                "SQL_BIG_RESULT",
1478                "SQL_BUFFER_RESULT",
1479                "SQL_NO_CACHE",
1480                "SQL_CALC_FOUND_ROWS",
1481            ];
1482            loop {
1483                if self.check(TokenType::StraightJoin) {
1484                    self.skip();
1485                    operation_modifiers.push("STRAIGHT_JOIN".to_string());
1486                } else if self.check(TokenType::Var) {
1487                    let upper = self.peek().text.to_ascii_uppercase();
1488                    if MYSQL_MODIFIERS.contains(&upper.as_str()) {
1489                        self.skip();
1490                        operation_modifiers.push(upper);
1491                    } else {
1492                        break;
1493                    }
1494                } else {
1495                    break;
1496                }
1497            }
1498        }
1499
1500        // Parse BigQuery SELECT AS STRUCT / SELECT AS VALUE
1501        let kind = if self.match_token(TokenType::As) {
1502            if self.match_identifier("STRUCT") {
1503                Some("STRUCT".to_string())
1504            } else if self.match_identifier("VALUE") {
1505                Some("VALUE".to_string())
1506            } else {
1507                // Not AS STRUCT/VALUE, backtrack the AS token
1508                self.current -= 1;
1509                None
1510            }
1511        } else {
1512            None
1513        };
1514
1515        // Parse select expressions
1516        let mut expressions = self.parse_select_expressions()?;
1517
1518        // Redshift: EXCLUDE clause at the end of the projection list
1519        // e.g., SELECT *, 4 AS col4 EXCLUDE (col2, col3) FROM ...
1520        // e.g., SELECT col1, *, col2 EXCLUDE(col3) FROM ...
1521        // e.g., SELECT *, 4 AS col4 EXCLUDE col2, col3 FROM ...
1522        // In Python sqlglot, this is handled by overriding _parse_projections in the Redshift parser.
1523        // The EXCLUDE clause is separate from * EXCLUDE — it applies to the entire projection list.
1524        let exclude = if matches!(
1525            self.config.dialect,
1526            Some(crate::dialects::DialectType::Redshift)
1527        ) {
1528            // Check if previous token was EXCLUDE (parsed as implicit alias).
1529            // e.g., SELECT *, 4 AS col4 EXCLUDE col2, col3 FROM ...
1530            //   → "col4 EXCLUDE" was parsed as (col4 aliased-as EXCLUDE), then "col2" as next projection
1531            //   → We need to strip the EXCLUDE alias from the last projection and retreat
1532            // Also handle: EXCLUDE was consumed as a bare column name if no AS was present
1533            let mut retreat_for_exclude = false;
1534            if let Some(last_expr) = expressions.last() {
1535                // Case: "4 AS col4 EXCLUDE" without parens — parsed as separate column "EXCLUDE"
1536                // Actually with the comma break, this won't happen. But "col2 EXCLUDE(col3)" might.
1537                match last_expr {
1538                    Expression::Alias(alias)
1539                        if alias.alias.name.eq_ignore_ascii_case("EXCLUDE") =>
1540                    {
1541                        // The last expression is "something AS EXCLUDE" or implicit alias EXCLUDE
1542                        // Strip the alias and check if EXCLUDE is followed by paren or identifier
1543                        if self.check(TokenType::LParen)
1544                            || self.is_identifier_token()
1545                            || self.is_safe_keyword_as_identifier()
1546                        {
1547                            // Strip the EXCLUDE alias from the last expression
1548                            let stripped = alias.this.clone();
1549                            if let Some(last) = expressions.last_mut() {
1550                                *last = stripped;
1551                            }
1552                            retreat_for_exclude = true;
1553                        }
1554                    }
1555                    _ => {}
1556                }
1557            }
1558
1559            if retreat_for_exclude || self.check(TokenType::Exclude) {
1560                if !retreat_for_exclude {
1561                    self.skip(); // consume EXCLUDE
1562                }
1563                // Parse EXCLUDE columns - with or without parens
1564                let mut exclude_cols = Vec::new();
1565                if self.match_token(TokenType::LParen) {
1566                    // Parenthesized list: EXCLUDE (col1, col2, ...)
1567                    loop {
1568                        let col_expr = self.parse_expression()?;
1569                        exclude_cols.push(col_expr);
1570                        if !self.match_token(TokenType::Comma) {
1571                            break;
1572                        }
1573                    }
1574                    self.match_token(TokenType::RParen);
1575                } else {
1576                    // Non-parenthesized: EXCLUDE col1, col2, ...
1577                    // Parse comma-separated identifiers until FROM or other clause boundary
1578                    loop {
1579                        if self.is_at_end()
1580                            || self.check(TokenType::From)
1581                            || self.check(TokenType::Where)
1582                            || self.check(TokenType::Semicolon)
1583                            || self.check(TokenType::RParen)
1584                        {
1585                            break;
1586                        }
1587                        let col_expr = self.parse_expression()?;
1588                        exclude_cols.push(col_expr);
1589                        if !self.match_token(TokenType::Comma) {
1590                            break;
1591                        }
1592                    }
1593                }
1594                if exclude_cols.is_empty() {
1595                    None
1596                } else {
1597                    Some(exclude_cols)
1598                }
1599            } else {
1600                None
1601            }
1602        } else {
1603            None
1604        };
1605
1606        // Parse INTO clause (SELECT ... INTO [TEMPORARY|UNLOGGED] table_name)
1607        // Also handles Oracle PL/SQL: BULK COLLECT INTO v1, v2, ...
1608        let into = if self.match_text_seq(&["BULK", "COLLECT", "INTO"]) {
1609            // Oracle PL/SQL: BULK COLLECT INTO var1, var2, ...
1610            // Parse target variables as a comma-separated list
1611            let mut target_expressions = vec![self.parse_expression()?];
1612            while self.match_token(TokenType::Comma) {
1613                target_expressions.push(self.parse_expression()?);
1614            }
1615            if target_expressions.len() == 1 {
1616                Some(SelectInto {
1617                    this: target_expressions.remove(0),
1618                    temporary: false,
1619                    unlogged: false,
1620                    bulk_collect: true,
1621                    expressions: Vec::new(),
1622                })
1623            } else {
1624                // Multiple targets - use first as `this` and rest as `expressions`
1625                // Actually, to match Python sqlglot behavior, store all in expressions
1626                Some(SelectInto {
1627                    this: Expression::Null(Null),
1628                    temporary: false,
1629                    unlogged: false,
1630                    bulk_collect: true,
1631                    expressions: target_expressions,
1632                })
1633            }
1634        } else if self.match_token(TokenType::Into) {
1635            // Check for TEMPORARY/TEMP/UNLOGGED keyword (PostgreSQL)
1636            let temporary = self.match_token(TokenType::Temporary) || self.match_identifier("TEMP");
1637            let unlogged = !temporary && self.match_identifier("UNLOGGED");
1638            // Parse first target (table name or PL/SQL variable)
1639            let table_name = self.parse_table_ref()?;
1640            // Oracle PL/SQL: SELECT ... INTO var1, var2, ... FROM ...
1641            // If followed by comma, parse additional target variables
1642            if self.match_token(TokenType::Comma) {
1643                let mut target_expressions = vec![Expression::Table(Box::new(table_name))];
1644                target_expressions.push(self.parse_expression()?);
1645                while self.match_token(TokenType::Comma) {
1646                    target_expressions.push(self.parse_expression()?);
1647                }
1648                Some(SelectInto {
1649                    this: Expression::Null(Null),
1650                    temporary,
1651                    unlogged,
1652                    bulk_collect: false,
1653                    expressions: target_expressions,
1654                })
1655            } else {
1656                Some(SelectInto {
1657                    this: Expression::Table(Box::new(table_name)),
1658                    temporary,
1659                    unlogged,
1660                    bulk_collect: false,
1661                    expressions: Vec::new(),
1662                })
1663            }
1664        } else {
1665            None
1666        };
1667
1668        // Parse FROM clause
1669        let from = if self.match_token(TokenType::From) {
1670            Some(self.parse_from()?)
1671        } else {
1672            None
1673        };
1674
1675        // Parse JOINs
1676        let mut joins = self.parse_joins()?;
1677
1678        // Handle PIVOT/UNPIVOT that comes after JOINs (e.g., SELECT * FROM a JOIN b ON ... PIVOT(...))
1679        // Store PIVOT/UNPIVOT in the last join's pivots field (this matches SQLGlot's semantics)
1680        while self.check(TokenType::Pivot) || self.check(TokenType::Unpivot) {
1681            if !joins.is_empty() {
1682                let last_idx = joins.len() - 1;
1683                // Parse the pivot/unpivot and store in the join's pivots vector
1684                // We pass a Null expression as the `this` since the pivot applies to the entire join result
1685                if self.match_token(TokenType::Pivot) {
1686                    let pivot = self.parse_pivot(Expression::Null(crate::expressions::Null))?;
1687                    joins[last_idx].pivots.push(pivot);
1688                } else if self.match_token(TokenType::Unpivot) {
1689                    let unpivot = self.parse_unpivot(Expression::Null(crate::expressions::Null))?;
1690                    joins[last_idx].pivots.push(unpivot);
1691                }
1692            } else {
1693                // No joins - break to avoid infinite loop
1694                break;
1695            }
1696        }
1697
1698        // Parse LATERAL VIEW clauses (Hive/Spark)
1699        let lateral_views = self.parse_lateral_views()?;
1700
1701        // Parse PREWHERE clause (ClickHouse specific)
1702        let prewhere = if self.match_token(TokenType::Prewhere) {
1703            Some(self.parse_expression()?)
1704        } else {
1705            None
1706        };
1707
1708        // Parse WHERE clause
1709        let mut where_clause = if self.match_token(TokenType::Where) {
1710            Some(Where {
1711                this: self.parse_expression()?,
1712            })
1713        } else {
1714            None
1715        };
1716
1717        // Parse CONNECT BY clause (Oracle hierarchical queries)
1718        let connect = self.parse_connect()?;
1719
1720        // Parse GROUP BY
1721        let group_by = if self.check(TokenType::Group) {
1722            let group_comments = self.current_leading_comments().to_vec();
1723            if self.match_keywords(&[TokenType::Group, TokenType::By]) {
1724                let mut gb = self.parse_group_by()?;
1725                gb.comments = group_comments;
1726                Some(gb)
1727            } else {
1728                None
1729            }
1730        } else if matches!(
1731            self.config.dialect,
1732            Some(crate::dialects::DialectType::ClickHouse)
1733        ) && self.check(TokenType::With)
1734            && (self.check_next_identifier("TOTALS")
1735                || self.check_next(TokenType::Rollup)
1736                || self.check_next(TokenType::Cube))
1737        {
1738            // ClickHouse: WITH TOTALS/ROLLUP/CUBE without GROUP BY
1739            self.skip(); // consume WITH
1740            let totals = self.match_identifier("TOTALS");
1741            let mut expressions = Vec::new();
1742            if self.match_token(TokenType::Rollup) {
1743                expressions.push(Expression::Rollup(Box::new(Rollup {
1744                    expressions: Vec::new(),
1745                })));
1746            } else if self.match_token(TokenType::Cube) {
1747                expressions.push(Expression::Cube(Box::new(Cube {
1748                    expressions: Vec::new(),
1749                })));
1750            }
1751            // Check for chained WITH TOTALS after WITH ROLLUP/CUBE
1752            if !totals && self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
1753                self.skip();
1754                self.skip();
1755            }
1756            Some(GroupBy {
1757                expressions,
1758                all: None,
1759                totals,
1760                comments: Vec::new(),
1761            })
1762        } else {
1763            None
1764        };
1765
1766        // Parse HAVING
1767        let having = if self.check(TokenType::Having) {
1768            let having_comments = self.current_leading_comments().to_vec();
1769            self.skip(); // consume HAVING
1770            Some(Having {
1771                this: self.parse_expression()?,
1772                comments: having_comments,
1773            })
1774        } else {
1775            None
1776        };
1777
1778        // Parse QUALIFY clause (Snowflake, BigQuery, DuckDB)
1779        // QUALIFY can appear before or after WINDOW clause
1780        let mut qualify = if self.match_token(TokenType::Qualify) {
1781            Some(Qualify {
1782                this: self.parse_expression()?,
1783            })
1784        } else {
1785            None
1786        };
1787
1788        // Parse WINDOW clause (named windows)
1789        // Only match WINDOW if followed by identifier AS ( (a real window definition)
1790        // Otherwise "window" may be a table alias (e.g., SELECT * FROM foo window)
1791        let windows = if self.check(TokenType::Window) && {
1792            let next_pos = self.current + 1;
1793            next_pos < self.tokens.len()
1794                && (self.tokens[next_pos].token_type == TokenType::Var
1795                    || self.tokens[next_pos].token_type == TokenType::Identifier)
1796        } {
1797            self.skip(); // consume WINDOW
1798            Some(self.parse_named_windows()?)
1799        } else {
1800            None
1801        };
1802
1803        // QUALIFY can also appear after WINDOW clause (DuckDB)
1804        let qualify_after_window = if qualify.is_none() && self.match_token(TokenType::Qualify) {
1805            qualify = Some(Qualify {
1806                this: self.parse_expression()?,
1807            });
1808            true
1809        } else {
1810            false
1811        };
1812
1813        // Parse DISTRIBUTE BY (Hive/Spark) - comes before SORT BY
1814        let distribute_by = if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
1815            Some(self.parse_distribute_by()?)
1816        } else {
1817            None
1818        };
1819
1820        // Parse CLUSTER BY (Hive/Spark)
1821        let cluster_by = if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
1822            Some(self.parse_cluster_by()?)
1823        } else {
1824            None
1825        };
1826
1827        // Parse SORT BY (Hive/Spark) - can come before ORDER BY
1828        let sort_by = if self.match_keywords(&[TokenType::Sort, TokenType::By]) {
1829            Some(self.parse_sort_by()?)
1830        } else {
1831            None
1832        };
1833
1834        // Parse ORDER BY or ORDER SIBLINGS BY (Oracle) - comes after SORT BY
1835        let order_by = if self.check(TokenType::Order) {
1836            let order_comments = self.current_leading_comments().to_vec();
1837            if self.match_keywords(&[TokenType::Order, TokenType::Siblings, TokenType::By]) {
1838                // ORDER SIBLINGS BY (Oracle hierarchical queries)
1839                let mut ob = self.parse_order_by_with_siblings(true)?;
1840                ob.comments = order_comments;
1841                Some(ob)
1842            } else if self.match_keywords(&[TokenType::Order, TokenType::By]) {
1843                let mut ob = self.parse_order_by()?;
1844                ob.comments = order_comments;
1845                Some(ob)
1846            } else {
1847                None
1848            }
1849        } else {
1850            None
1851        };
1852
1853        // Parse LIMIT (supports MySQL syntax: LIMIT offset, count)
1854        // DuckDB supports: LIMIT 10 PERCENT or LIMIT 10%
1855        // Capture trailing comments from the token before LIMIT (e.g., WHERE condition's last token)
1856        // These comments should be emitted after the LIMIT value, not before LIMIT.
1857        let pre_limit_comments = if self.check(TokenType::Limit) {
1858            let mut comments = self.previous_trailing_comments().to_vec();
1859            // Also capture leading comments on the LIMIT token (comments on a separate line before LIMIT)
1860            comments.extend_from_slice(self.current_leading_comments());
1861            comments
1862        } else {
1863            Vec::new()
1864        };
1865        let (limit, offset) = if self.match_token(TokenType::Limit) {
1866            // Clear the pre-LIMIT comments from the WHERE condition expression to avoid duplication
1867            if !pre_limit_comments.is_empty() {
1868                if let Some(ref mut w) = where_clause {
1869                    Self::clear_rightmost_trailing_comments(&mut w.this);
1870                }
1871            }
1872            // First try parse_unary to check for PERCENT/% modifier.
1873            // This avoids parse_expression consuming % as the modulo operator.
1874            // Both "PERCENT" and "%" tokens have TokenType::Percent, but we need to
1875            // distinguish PERCENT-as-modifier from %-as-modulo. "%" is PERCENT when
1876            // followed by a clause boundary (OFFSET, end, semicolon, etc.).
1877            let saved_pos = self.current;
1878            let (first_expr, has_percent) = {
1879                let unary_result = self.parse_unary();
1880                match unary_result {
1881                    Ok(expr) => {
1882                        if self.check(TokenType::Percent) && self.is_percent_modifier() {
1883                            // Found PERCENT keyword or % symbol used as PERCENT modifier
1884                            self.skip();
1885                            (expr, true)
1886                        } else {
1887                            // No PERCENT - backtrack and use full parse_expression
1888                            self.current = saved_pos;
1889                            let full_expr = self.parse_expression()?;
1890                            // Check again for PERCENT keyword (e.g., after complex expression)
1891                            let has_pct =
1892                                if self.check(TokenType::Percent) && self.is_percent_modifier() {
1893                                    self.skip();
1894                                    true
1895                                } else {
1896                                    false
1897                                };
1898                            (full_expr, has_pct)
1899                        }
1900                    }
1901                    Err(_) => {
1902                        // Unary parsing failed - backtrack and use parse_expression
1903                        self.current = saved_pos;
1904                        let full_expr = self.parse_expression()?;
1905                        let has_pct =
1906                            if self.check(TokenType::Percent) && self.is_percent_modifier() {
1907                                self.skip();
1908                                true
1909                            } else {
1910                                false
1911                            };
1912                        (full_expr, has_pct)
1913                    }
1914                }
1915            };
1916            // MySQL syntax: LIMIT offset, count
1917            if self.match_token(TokenType::Comma) {
1918                let second_expr = self.parse_expression()?;
1919                // First expression is offset, second is count
1920                (
1921                    Some(Limit {
1922                        this: second_expr,
1923                        percent: false,
1924                        comments: pre_limit_comments.clone(),
1925                    }),
1926                    Some(Offset {
1927                        this: first_expr,
1928                        rows: None,
1929                    }),
1930                )
1931            } else {
1932                // Standard: LIMIT count [PERCENT]
1933                (
1934                    Some(Limit {
1935                        this: first_expr,
1936                        percent: has_percent,
1937                        comments: pre_limit_comments,
1938                    }),
1939                    None,
1940                )
1941            }
1942        } else {
1943            (None, None)
1944        };
1945
1946        // WITH TIES after LIMIT (ClickHouse, DuckDB)
1947        if limit.is_some() {
1948            let _ = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1949        }
1950
1951        // Parse OFFSET (if not already parsed from MySQL LIMIT syntax)
1952        // Standard SQL syntax: OFFSET n [ROW|ROWS]
1953        // Some dialects (Presto/Trino) support: OFFSET n LIMIT m
1954        let (limit, offset) = if offset.is_none() && self.match_token(TokenType::Offset) {
1955            let expr = self.parse_expression()?;
1956            // Consume optional ROW or ROWS keyword and track it
1957            let rows = if self.match_token(TokenType::Row) || self.match_token(TokenType::Rows) {
1958                Some(true)
1959            } else {
1960                None
1961            };
1962            let offset = Some(Offset { this: expr, rows });
1963
1964            // Check for LIMIT after OFFSET (Presto/Trino syntax: OFFSET n LIMIT m)
1965            let limit = if limit.is_none() && self.match_token(TokenType::Limit) {
1966                let limit_expr = self.parse_expression()?;
1967                Some(Limit {
1968                    this: limit_expr,
1969                    percent: false,
1970                    comments: Vec::new(),
1971                })
1972            } else {
1973                limit
1974            };
1975
1976            (limit, offset)
1977        } else {
1978            (limit, offset)
1979        };
1980
1981        // ClickHouse: LIMIT ... BY expressions
1982        let limit_by = if matches!(
1983            self.config.dialect,
1984            Some(crate::dialects::DialectType::ClickHouse)
1985        ) && limit.is_some()
1986            && self.match_token(TokenType::By)
1987        {
1988            let expressions = self.parse_expression_list()?;
1989            if expressions.is_empty() {
1990                return Err(self.parse_error("Expected expression after LIMIT BY"));
1991            }
1992            Some(expressions)
1993        } else {
1994            None
1995        };
1996
1997        // ClickHouse: second LIMIT after LIMIT BY (LIMIT n BY expr LIMIT m)
1998        // Also supports LIMIT offset, count syntax
1999        let (limit, offset) = if limit_by.is_some() && self.match_token(TokenType::Limit) {
2000            let first_expr = self.parse_expression()?;
2001            if self.match_token(TokenType::Comma) {
2002                // LIMIT offset, count
2003                let count_expr = self.parse_expression()?;
2004                (
2005                    Some(Limit {
2006                        this: count_expr,
2007                        percent: false,
2008                        comments: Vec::new(),
2009                    }),
2010                    Some(Offset {
2011                        this: first_expr,
2012                        rows: None,
2013                    }),
2014                )
2015            } else {
2016                (
2017                    Some(Limit {
2018                        this: first_expr,
2019                        percent: false,
2020                        comments: Vec::new(),
2021                    }),
2022                    offset,
2023                )
2024            }
2025        } else {
2026            (limit, offset)
2027        };
2028
2029        // Parse FETCH FIRST/NEXT clause
2030        let fetch = if self.match_token(TokenType::Fetch) {
2031            Some(self.parse_fetch()?)
2032        } else {
2033            None
2034        };
2035
2036        // Parse SAMPLE / TABLESAMPLE clause
2037        let sample = self.parse_sample_clause()?;
2038
2039        // Parse FOR UPDATE/SHARE locks or FOR XML (T-SQL)
2040        let (locks, for_xml) = self.parse_locks_and_for_xml()?;
2041
2042        // TSQL: OPTION clause (e.g., OPTION(LABEL = 'foo', HASH JOIN))
2043        let option = if self.check_identifier("OPTION") && self.check_next(TokenType::LParen) {
2044            self.skip(); // consume OPTION
2045            self.skip(); // consume (
2046            let mut content = String::from("OPTION(");
2047            let mut depth = 1;
2048            while !self.is_at_end() && depth > 0 {
2049                let tok = self.advance();
2050                if tok.token_type == TokenType::LParen {
2051                    depth += 1;
2052                } else if tok.token_type == TokenType::RParen {
2053                    depth -= 1;
2054                }
2055                if depth > 0 {
2056                    if tok.token_type == TokenType::String {
2057                        if content.len() > 7 && !content.ends_with('(') && !content.ends_with(' ') {
2058                            content.push(' ');
2059                        }
2060                        content.push('\'');
2061                        content.push_str(&tok.text.replace('\'', "''"));
2062                        content.push('\'');
2063                    } else if tok.token_type == TokenType::Eq {
2064                        content.push_str(" = ");
2065                    } else if tok.token_type == TokenType::Comma {
2066                        content.push_str(", ");
2067                    } else {
2068                        if content.len() > 7 && !content.ends_with('(') && !content.ends_with(' ') {
2069                            content.push(' ');
2070                        }
2071                        content.push_str(&tok.text);
2072                    }
2073                }
2074            }
2075            content.push(')');
2076            Some(content)
2077        } else {
2078            None
2079        };
2080
2081        // ClickHouse: SETTINGS and FORMAT clauses after LIMIT/OFFSET/FETCH
2082        let (settings, format) = if matches!(
2083            self.config.dialect,
2084            Some(crate::dialects::DialectType::ClickHouse)
2085        ) {
2086            let mut settings: Option<Vec<Expression>> = None;
2087            let mut format: Option<Expression> = None;
2088
2089            loop {
2090                if settings.is_none() && self.match_token(TokenType::Settings) {
2091                    let mut settings_exprs = Vec::new();
2092                    loop {
2093                        settings_exprs.push(self.parse_expression()?);
2094                        if !self.match_token(TokenType::Comma) {
2095                            break;
2096                        }
2097                    }
2098                    settings = Some(settings_exprs);
2099                    continue;
2100                }
2101
2102                if format.is_none() && self.match_token(TokenType::Format) {
2103                    // ClickHouse: FORMAT Null is valid (Null is a keyword token, not an identifier)
2104                    let ident = if self.check(TokenType::Null) {
2105                        let text = self.advance().text;
2106                        Identifier::new(text)
2107                    } else {
2108                        self.expect_identifier_or_keyword_with_quoted()?
2109                    };
2110                    format = Some(Expression::Identifier(ident));
2111                    // ClickHouse: FORMAT <name> may be followed by inline data
2112                    // (CSV rows, JSON objects, etc.) — consume to semicolon
2113                    if matches!(
2114                        self.config.dialect,
2115                        Some(crate::dialects::DialectType::ClickHouse)
2116                    ) && !self.is_at_end()
2117                        && !self.check(TokenType::Semicolon)
2118                        && !self.check(TokenType::Settings)
2119                    {
2120                        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
2121                            self.skip();
2122                        }
2123                    }
2124                    continue;
2125                }
2126
2127                break;
2128            }
2129
2130            (settings, format)
2131        } else {
2132            (None, None)
2133        };
2134
2135        let select = Select {
2136            expressions,
2137            from,
2138            joins,
2139            lateral_views,
2140            prewhere,
2141            where_clause,
2142            group_by,
2143            having,
2144            qualify,
2145            order_by,
2146            distribute_by,
2147            cluster_by,
2148            sort_by,
2149            limit,
2150            offset,
2151            limit_by,
2152            fetch,
2153            distinct,
2154            distinct_on,
2155            top,
2156            with: None,
2157            sample,
2158            settings,
2159            format,
2160            windows,
2161            hint,
2162            connect,
2163            into,
2164            locks,
2165            for_xml,
2166            leading_comments,
2167            post_select_comments,
2168            kind,
2169            operation_modifiers,
2170            qualify_after_window,
2171            option,
2172            exclude,
2173        };
2174
2175        // Check for set operations (UNION, INTERSECT, EXCEPT)
2176        let result = Expression::Select(Box::new(select));
2177        self.parse_set_operation(result)
2178    }
2179
2180    /// Parse a WITH clause (CTEs)
2181    fn parse_with(&mut self) -> Result<Expression> {
2182        use crate::dialects::DialectType;
2183
2184        let with_token = self.expect(TokenType::With)?;
2185        let leading_comments = with_token.comments;
2186
2187        let recursive = self.match_token(TokenType::Recursive);
2188        let mut ctes = Vec::new();
2189
2190        loop {
2191            // ClickHouse supports expression-first WITH items:
2192            // WITH <expr> AS <alias> SELECT ...
2193            if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
2194                let saved_pos = self.current;
2195                if let Ok(expr) = self.parse_expression() {
2196                    // Check if parse_expression already consumed the AS alias
2197                    // (e.g., `(1, 2) AS a` gets parsed as Alias(Tuple, "a") by the tuple alias handler)
2198                    let (inner_expr, alias_opt) = if let Expression::Alias(ref alias_box) = expr {
2199                        (alias_box.this.clone(), Some(alias_box.alias.clone()))
2200                    } else {
2201                        (expr, None)
2202                    };
2203
2204                    if let Some(alias) = alias_opt {
2205                        // Expression already had AS alias consumed
2206                        ctes.push(Cte {
2207                            alias,
2208                            this: inner_expr,
2209                            columns: Vec::new(),
2210                            materialized: None,
2211                            key_expressions: Vec::new(),
2212                            alias_first: false,
2213                            comments: Vec::new(),
2214                        });
2215
2216                        if self.match_token(TokenType::Comma) {
2217                            continue;
2218                        }
2219                        break;
2220                    } else if self.match_token(TokenType::As)
2221                        && self.is_identifier_or_keyword_token()
2222                    {
2223                        // Require AS <alias> to disambiguate from standard CTE syntax
2224                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
2225                        ctes.push(Cte {
2226                            alias,
2227                            this: inner_expr,
2228                            columns: Vec::new(),
2229                            materialized: None,
2230                            key_expressions: Vec::new(),
2231                            alias_first: false,
2232                            comments: Vec::new(),
2233                        });
2234
2235                        if self.match_token(TokenType::Comma) {
2236                            continue;
2237                        }
2238                        break;
2239                    } else if self.check(TokenType::Select) || self.check(TokenType::Comma) {
2240                        // ClickHouse: WITH expr SELECT ... (unaliased expression in CTE)
2241                        ctes.push(Cte {
2242                            alias: Identifier::new(format!("{}", inner_expr)),
2243                            this: inner_expr,
2244                            columns: Vec::new(),
2245                            materialized: None,
2246                            key_expressions: Vec::new(),
2247                            alias_first: false,
2248                            comments: Vec::new(),
2249                        });
2250
2251                        if self.match_token(TokenType::Comma) {
2252                            continue;
2253                        }
2254                        break;
2255                    }
2256                }
2257                // Fall back to standard CTE parsing
2258                self.current = saved_pos;
2259            }
2260
2261            // CTE names can be keywords like 'view', 'use', 'all', etc.
2262            let name = self.expect_identifier_or_alias_keyword_with_quoted()?;
2263
2264            // Optional column list
2265            // But first check for Snowflake-style CTE: WITH t (SELECT ...) - no AS keyword
2266            // In that case, LParen is followed by SELECT, not column names
2267            let columns = if self.check(TokenType::LParen) && !self.check_next(TokenType::Select) {
2268                self.skip(); // consume LParen
2269                let cols = self.parse_identifier_list()?;
2270                self.expect(TokenType::RParen)?;
2271                cols
2272            } else {
2273                Vec::new()
2274            };
2275
2276            // Optional USING KEY (columns) for DuckDB recursive CTEs
2277            let key_expressions = if self.match_keywords(&[TokenType::Using, TokenType::Key]) {
2278                self.expect(TokenType::LParen)?;
2279                let keys = self.parse_identifier_list()?;
2280                self.expect(TokenType::RParen)?;
2281                keys
2282            } else {
2283                Vec::new()
2284            };
2285
2286            // ClickHouse: keyword -> body AS alias (single-param lambda where param is a keyword)
2287            // e.g., WITH time -> sin(time * 2 * pi()) AS sine_wave
2288            if matches!(self.config.dialect, Some(DialectType::ClickHouse))
2289                && self.check(TokenType::Arrow)
2290            {
2291                self.skip(); // consume ->
2292                let body = self.parse_expression()?;
2293                let lambda = Expression::Lambda(Box::new(LambdaExpr {
2294                    parameters: vec![name.clone()],
2295                    body,
2296                    colon: false,
2297                    parameter_types: Vec::new(),
2298                }));
2299                // Expect AS alias
2300                if self.match_token(TokenType::As) && self.is_identifier_or_keyword_token() {
2301                    let alias = self.expect_identifier_or_keyword_with_quoted()?;
2302                    ctes.push(Cte {
2303                        alias,
2304                        this: lambda,
2305                        columns: Vec::new(),
2306                        materialized: None,
2307                        key_expressions: Vec::new(),
2308                        alias_first: false,
2309                        comments: Vec::new(),
2310                    });
2311                } else {
2312                    // Unaliased lambda CTE
2313                    ctes.push(Cte {
2314                        alias: name,
2315                        this: lambda,
2316                        columns: Vec::new(),
2317                        materialized: None,
2318                        key_expressions: Vec::new(),
2319                        alias_first: false,
2320                        comments: Vec::new(),
2321                    });
2322                }
2323                if self.match_token(TokenType::Comma) {
2324                    continue;
2325                }
2326                break;
2327            }
2328
2329            // AS is optional (Snowflake allows WITH t (SELECT ...) without AS)
2330            let cte_comments = if self.match_token(TokenType::As) {
2331                // Capture trailing comments from the AS token
2332                // e.g., "WITH a AS /* comment */ (...)" -> comment goes after alias
2333                self.previous_trailing_comments().to_vec()
2334            } else {
2335                Vec::new()
2336            };
2337
2338            // Check for MATERIALIZED or NOT MATERIALIZED
2339            let materialized = if self.match_token(TokenType::Materialized) {
2340                Some(true)
2341            } else if self.match_token(TokenType::Not) {
2342                self.expect(TokenType::Materialized)?;
2343                Some(false)
2344            } else {
2345                None
2346            };
2347
2348            self.expect(TokenType::LParen)?;
2349            let query = self.parse_statement()?;
2350            self.expect(TokenType::RParen)?;
2351
2352            ctes.push(Cte {
2353                alias: name,
2354                this: query,
2355                columns,
2356                materialized,
2357                key_expressions,
2358                alias_first: true,
2359                comments: cte_comments,
2360            });
2361
2362            if !self.match_token(TokenType::Comma) {
2363                // Check for WITH merging: WITH a AS (...) WITH b AS (...) -> merged
2364                // If the next token is WITH (not followed by nothing), continue parsing CTEs
2365                if self.check(TokenType::With) {
2366                    self.skip(); // consume the redundant WITH keyword
2367                                 // Check if this WITH is also RECURSIVE
2368                    if self.match_token(TokenType::Recursive) && !recursive {
2369                        // If second WITH is RECURSIVE but first wasn't, ignore (keep non-recursive)
2370                    }
2371                    continue; // continue the loop to parse more CTEs
2372                }
2373                break;
2374            }
2375            // WI-14f: Skip redundant WITH keyword after comma in CTE list
2376            // e.g., WITH a AS (SELECT 1), WITH b AS (SELECT 2) SELECT *
2377            self.match_token(TokenType::With);
2378        }
2379
2380        // Parse optional SEARCH/CYCLE clause for recursive CTEs (PostgreSQL)
2381        // Syntax: SEARCH BREADTH|DEPTH FIRST BY column SET column [USING column]
2382        //     or: CYCLE column SET column USING column
2383        let search = self.parse_recursive_with_search()?;
2384
2385        // Parse the main query
2386        let mut main_query = self.parse_statement()?;
2387
2388        // Unwrap parenthesized wrappers to find the inner SELECT
2389        // (matching Python sqlglot: while isinstance(this, Subquery) and this.is_wrapper)
2390        loop {
2391            match main_query {
2392                Expression::Paren(paren) => {
2393                    main_query = paren.this;
2394                }
2395                Expression::Subquery(ref sub)
2396                    if sub.alias.is_none()
2397                        && sub.order_by.is_none()
2398                        && sub.limit.is_none()
2399                        && sub.offset.is_none() =>
2400                {
2401                    // Unwrap Subquery wrapper (parenthesized query without modifiers)
2402                    if let Expression::Subquery(sub) = main_query {
2403                        main_query = sub.this;
2404                    } else {
2405                        break;
2406                    }
2407                }
2408                _ => break,
2409            }
2410        }
2411
2412        // Attach WITH to the main query
2413        let with_clause = With {
2414            ctes,
2415            recursive,
2416            leading_comments,
2417            search,
2418        };
2419        match &mut main_query {
2420            Expression::Select(ref mut select) => {
2421                select.with = Some(with_clause);
2422            }
2423            Expression::Union(ref mut union) => {
2424                union.with = Some(with_clause);
2425            }
2426            Expression::Intersect(ref mut intersect) => {
2427                intersect.with = Some(with_clause);
2428            }
2429            Expression::Except(ref mut except) => {
2430                except.with = Some(with_clause);
2431            }
2432            Expression::Update(ref mut update) => {
2433                update.with = Some(with_clause);
2434            }
2435            Expression::Insert(ref mut insert) => {
2436                insert.with = Some(with_clause);
2437            }
2438            Expression::Delete(ref mut delete) => {
2439                delete.with = Some(with_clause);
2440            }
2441            Expression::CreateTable(ref mut ct) => {
2442                ct.with_cte = Some(with_clause);
2443            }
2444            Expression::Pivot(ref mut pivot) => {
2445                pivot.with = Some(with_clause);
2446            }
2447            _ => {}
2448        }
2449
2450        Ok(main_query)
2451    }
2452
2453    /// Parse SELECT expressions
2454    fn parse_select_expressions(&mut self) -> Result<Vec<Expression>> {
2455        let mut expressions = Vec::new();
2456
2457        loop {
2458            // Check if we're at end of select list (empty list case for TSQL TOP)
2459            // This allows queries like "SELECT TOP 10 PERCENT" with no columns
2460            // Also check for Oracle BULK COLLECT INTO sequence
2461            // ClickHouse: minus() is tokenized as Except but should be treated as function
2462            let is_ch_keyword_func = matches!(
2463                self.config.dialect,
2464                Some(crate::dialects::DialectType::ClickHouse)
2465            ) && (self.check(TokenType::Except)
2466                || self.check(TokenType::Intersect))
2467                && self.check_next(TokenType::LParen);
2468            // ClickHouse: `from`/`except` can be column names when followed by an operator
2469            // (e.g., `from + from`, `from in [0]`, `from, ...`)
2470            // Also: `from FROM t` — two consecutive FROM tokens means first is column name
2471            let is_ch_keyword_as_column = matches!(
2472                self.config.dialect,
2473                Some(crate::dialects::DialectType::ClickHouse)
2474            ) && (self.check(TokenType::From)
2475                || self.check(TokenType::Except))
2476                && {
2477                    let next_tt = self
2478                        .peek_nth(1)
2479                        .map(|t| t.token_type)
2480                        .unwrap_or(TokenType::Semicolon);
2481                    matches!(
2482                        next_tt,
2483                        TokenType::Plus | TokenType::Dash | TokenType::Star | TokenType::Slash
2484                        | TokenType::Percent | TokenType::Eq | TokenType::Neq | TokenType::Lt
2485                        | TokenType::Gt | TokenType::Lte | TokenType::Gte
2486                        | TokenType::And | TokenType::Or | TokenType::Comma | TokenType::Dot
2487                        | TokenType::In | TokenType::Is | TokenType::Not | TokenType::Like
2488                        | TokenType::Between | TokenType::Semicolon | TokenType::RParen
2489                        | TokenType::As | TokenType::DPipe | TokenType::Amp | TokenType::Pipe
2490                        | TokenType::LBracket
2491                        // Two consecutive FROM tokens: first is column name (e.g., SELECT from FROM t)
2492                        | TokenType::From
2493                    )
2494                };
2495            if !is_ch_keyword_func
2496                && !is_ch_keyword_as_column
2497                && (self.is_at_end()
2498                    || self.check(TokenType::From)
2499                    || self.check(TokenType::Where)
2500                    || self.check(TokenType::Into)
2501                    || self.check(TokenType::Union)
2502                    || self.check(TokenType::Intersect)
2503                    || self.check(TokenType::Except)
2504                    || self.check(TokenType::Order)
2505                    || self.check(TokenType::Limit)
2506                    || self.check(TokenType::Semicolon)
2507                    || self.check_text_seq(&["BULK", "COLLECT", "INTO"]))
2508            {
2509                break;
2510            }
2511
2512            // Handle star
2513            if self.check(TokenType::Star) {
2514                self.skip();
2515                let star_trailing_comments = self.previous_trailing_comments().to_vec();
2516                let star = self.parse_star_modifiers_with_comments(None, star_trailing_comments)?;
2517                let mut star_expr = Expression::Star(star);
2518                // ClickHouse: * APPLY(func) or * APPLY func or * APPLY(x -> expr) column transformer
2519                if matches!(
2520                    self.config.dialect,
2521                    Some(crate::dialects::DialectType::ClickHouse)
2522                ) {
2523                    while self.check(TokenType::Apply) {
2524                        self.skip(); // consume APPLY
2525                        let apply_expr = if self.match_token(TokenType::LParen) {
2526                            // Could be APPLY(func_name) or APPLY(x -> expr)
2527                            let expr = self.parse_expression()?;
2528                            self.expect(TokenType::RParen)?;
2529                            expr
2530                        } else {
2531                            // APPLY func or APPLY x -> expr (no parens)
2532                            // Parse as expression to handle lambdas
2533                            self.parse_expression()?
2534                        };
2535                        star_expr = Expression::Apply(Box::new(crate::expressions::Apply {
2536                            this: Box::new(star_expr),
2537                            expression: Box::new(apply_expr),
2538                        }));
2539                    }
2540                }
2541                // ClickHouse: Also handle EXCEPT/REPLACE between APPLYs:
2542                // * APPLY(toDate) EXCEPT(i, j) APPLY(any)
2543                if matches!(
2544                    self.config.dialect,
2545                    Some(crate::dialects::DialectType::ClickHouse)
2546                ) && (self.check(TokenType::Except)
2547                    || self.check(TokenType::Exclude)
2548                    || self.check(TokenType::Replace))
2549                {
2550                    // Consume EXCEPT/REPLACE modifiers after APPLY
2551                    self.parse_star_modifiers(None)?;
2552                    // Continue with more APPLYs
2553                    while self.check(TokenType::Apply) {
2554                        self.skip();
2555                        let apply_expr = if self.match_token(TokenType::LParen) {
2556                            let expr = self.parse_expression()?;
2557                            self.expect(TokenType::RParen)?;
2558                            expr
2559                        } else {
2560                            self.parse_expression()?
2561                        };
2562                        star_expr = Expression::Apply(Box::new(crate::expressions::Apply {
2563                            this: Box::new(star_expr),
2564                            expression: Box::new(apply_expr),
2565                        }));
2566                    }
2567                }
2568                // ClickHouse: * followed by operators (e.g., * IS NOT NULL, * AND expr)
2569                // Treat * as a regular expression and continue parsing operators
2570                if matches!(
2571                    self.config.dialect,
2572                    Some(crate::dialects::DialectType::ClickHouse)
2573                ) && matches!(
2574                    self.peek().token_type,
2575                    TokenType::Is
2576                        | TokenType::And
2577                        | TokenType::Or
2578                        | TokenType::Eq
2579                        | TokenType::Neq
2580                        | TokenType::Lt
2581                        | TokenType::Gt
2582                        | TokenType::Lte
2583                        | TokenType::Gte
2584                        | TokenType::Not
2585                        | TokenType::Plus
2586                        | TokenType::Dash
2587                        | TokenType::Slash
2588                        | TokenType::Percent
2589                        | TokenType::Like
2590                        | TokenType::Between
2591                        | TokenType::In
2592                ) {
2593                    // Re-parse from the operator with star_expr as the left side
2594                    let left = star_expr;
2595                    // Use parse_comparison / parse_is chain
2596                    if self.check(TokenType::Is) {
2597                        self.skip(); // consume IS
2598                        let not = self.match_token(TokenType::Not);
2599                        if self.match_token(TokenType::Null) {
2600                            star_expr = if not {
2601                                Expression::Not(Box::new(UnaryOp {
2602                                    this: Expression::Is(Box::new(BinaryOp::new(
2603                                        left,
2604                                        Expression::Null(Null),
2605                                    ))),
2606                                    inferred_type: None,
2607                                }))
2608                            } else {
2609                                Expression::Is(Box::new(BinaryOp::new(
2610                                    left,
2611                                    Expression::Null(Null),
2612                                )))
2613                            };
2614                        } else {
2615                            let right = self.parse_or()?;
2616                            star_expr = if not {
2617                                Expression::Not(Box::new(UnaryOp {
2618                                    this: Expression::Is(Box::new(BinaryOp::new(left, right))),
2619                                    inferred_type: None,
2620                                }))
2621                            } else {
2622                                Expression::Is(Box::new(BinaryOp::new(left, right)))
2623                            };
2624                        }
2625                    } else if self.match_token(TokenType::And) {
2626                        let right = self.parse_or()?;
2627                        star_expr = Expression::And(Box::new(BinaryOp::new(left, right)));
2628                    } else if self.match_token(TokenType::Or) {
2629                        let right = self.parse_or()?;
2630                        star_expr = Expression::Or(Box::new(BinaryOp::new(left, right)));
2631                    } else {
2632                        let op_token = self.advance();
2633                        let right = self.parse_or()?;
2634                        star_expr = match op_token.token_type {
2635                            TokenType::Eq => Expression::Eq(Box::new(BinaryOp::new(left, right))),
2636                            TokenType::Neq => Expression::Neq(Box::new(BinaryOp::new(left, right))),
2637                            TokenType::Lt => Expression::Lt(Box::new(BinaryOp::new(left, right))),
2638                            TokenType::Gt => Expression::Gt(Box::new(BinaryOp::new(left, right))),
2639                            TokenType::Lte => Expression::Lte(Box::new(BinaryOp::new(left, right))),
2640                            TokenType::Gte => Expression::Gte(Box::new(BinaryOp::new(left, right))),
2641                            TokenType::Plus => {
2642                                Expression::Add(Box::new(BinaryOp::new(left, right)))
2643                            }
2644                            TokenType::Dash => {
2645                                Expression::Sub(Box::new(BinaryOp::new(left, right)))
2646                            }
2647                            _ => left, // fallback
2648                        };
2649                    }
2650                }
2651                expressions.push(star_expr);
2652            } else {
2653                // Capture leading comments from the first token before parsing
2654                // These are comments on a separate line before the expression
2655                let leading_comments = self.current_leading_comments().to_vec();
2656                let expr = self.parse_expression()?;
2657
2658                // ClickHouse: COLUMNS(id, value) EXCEPT (id) REPLACE (5 AS id) APPLY func
2659                // Also: a.* APPLY(toDate) EXCEPT(i, j) APPLY(any) - qualified star with APPLY
2660                let expr = if matches!(
2661                    self.config.dialect,
2662                    Some(crate::dialects::DialectType::ClickHouse)
2663                ) {
2664                    let is_columns_func = match &expr {
2665                        Expression::Function(f) => f.name.eq_ignore_ascii_case("COLUMNS"),
2666                        Expression::MethodCall(m) => m.method.name.eq_ignore_ascii_case("COLUMNS"),
2667                        Expression::Columns(_) => true,
2668                        _ => false,
2669                    };
2670                    let is_qualified_star = matches!(&expr, Expression::Star(_));
2671                    if (is_columns_func || is_qualified_star)
2672                        && (self.check(TokenType::Except)
2673                            || self.check(TokenType::Exclude)
2674                            || self.check(TokenType::Replace)
2675                            || self.check(TokenType::Apply))
2676                    {
2677                        let mut result = expr;
2678                        // Parse any mix of EXCEPT/REPLACE/APPLY in any order
2679                        // e.g., * APPLY(toDate) EXCEPT(i, j) APPLY(any)
2680                        loop {
2681                            if self.check(TokenType::Except) || self.check(TokenType::Exclude) {
2682                                // Parse EXCEPT/EXCLUDE modifier
2683                                self.skip();
2684                                self.match_identifier("STRICT");
2685                                if self.match_token(TokenType::LParen) {
2686                                    loop {
2687                                        if self.check(TokenType::RParen) {
2688                                            break;
2689                                        }
2690                                        let _ = self.parse_expression()?;
2691                                        if !self.match_token(TokenType::Comma) {
2692                                            break;
2693                                        }
2694                                    }
2695                                    self.expect(TokenType::RParen)?;
2696                                } else if self.is_identifier_token()
2697                                    || self.is_safe_keyword_as_identifier()
2698                                {
2699                                    let _ = self.parse_expression()?;
2700                                }
2701                            } else if self.check(TokenType::Replace) {
2702                                // Parse REPLACE modifier: REPLACE (expr AS alias, ...)
2703                                self.skip();
2704                                self.match_identifier("STRICT");
2705                                if self.match_token(TokenType::LParen) {
2706                                    loop {
2707                                        if self.check(TokenType::RParen) {
2708                                            break;
2709                                        }
2710                                        let _ = self.parse_expression()?;
2711                                        if self.match_token(TokenType::As) {
2712                                            if self.is_identifier_token()
2713                                                || self.is_safe_keyword_as_identifier()
2714                                            {
2715                                                self.skip();
2716                                            }
2717                                        }
2718                                        if !self.match_token(TokenType::Comma) {
2719                                            break;
2720                                        }
2721                                    }
2722                                    self.expect(TokenType::RParen)?;
2723                                } else {
2724                                    let _ = self.parse_expression()?;
2725                                    if self.match_token(TokenType::As) {
2726                                        if self.is_identifier_token()
2727                                            || self.is_safe_keyword_as_identifier()
2728                                        {
2729                                            self.skip();
2730                                        }
2731                                    }
2732                                }
2733                            } else if self.check(TokenType::Apply) {
2734                                // Parse APPLY transformer
2735                                self.skip();
2736                                let apply_expr = if self.match_token(TokenType::LParen) {
2737                                    let e = self.parse_expression()?;
2738                                    self.expect(TokenType::RParen)?;
2739                                    e
2740                                } else {
2741                                    self.parse_expression()?
2742                                };
2743                                result = Expression::Apply(Box::new(crate::expressions::Apply {
2744                                    this: Box::new(result),
2745                                    expression: Box::new(apply_expr),
2746                                }));
2747                            } else {
2748                                break;
2749                            }
2750                        }
2751                        result
2752                    } else {
2753                        expr
2754                    }
2755                } else {
2756                    expr
2757                };
2758
2759                // Capture comments between expression and potential AS
2760                let pre_alias_comments = self.previous_trailing_comments().to_vec();
2761
2762                // DuckDB prefix alias syntax: identifier: expression (e.g., "foo: 1" means "1 AS foo")
2763                // Check if the expression is a simple identifier followed by a colon
2764                let expr = if self.check(TokenType::Colon) && !self.check_next(TokenType::Colon) {
2765                    // Extract the alias name from the identifier expression
2766                    let alias_ident = match &expr {
2767                        Expression::Identifier(id) => Some(id.clone()),
2768                        Expression::Column(col) if col.table.is_none() => Some(col.name.clone()),
2769                        _ => None,
2770                    };
2771                    if let Some(alias) = alias_ident {
2772                        // Consume the colon
2773                        self.skip();
2774                        let colon_comments = self.previous_trailing_comments().to_vec();
2775                        // Parse the actual value expression
2776                        let value = self.parse_expression()?;
2777                        let value_trailing = self.previous_trailing_comments().to_vec();
2778                        // For colon-alias (foo: expr), comments between alias and colon should
2779                        // become trailing comments (placed after the alias in output).
2780                        // Comments after the value expression are also trailing.
2781                        let mut all_trailing = pre_alias_comments.clone();
2782                        all_trailing.extend(colon_comments);
2783                        all_trailing.extend(value_trailing);
2784                        Expression::Alias(Box::new(Alias {
2785                            this: value,
2786                            alias,
2787                            column_aliases: Vec::new(),
2788                            pre_alias_comments: Vec::new(),
2789                            trailing_comments: all_trailing,
2790                            inferred_type: None,
2791                        }))
2792                    } else {
2793                        // Not a simple identifier, fall through to normal alias handling
2794                        // (this handles cases where the expression is complex before the colon)
2795                        expr
2796                    }
2797                } else if self.match_token(TokenType::As) {
2798                    // Capture comments from AS token (e.g., AS /* foo */ (a, b, c))
2799                    // These go into trailing_comments (after the alias), not pre_alias_comments
2800                    let as_comments = self.previous_trailing_comments().to_vec();
2801                    // Check for column aliases: AS (col1, col2) - used by POSEXPLODE etc.
2802                    if self.match_token(TokenType::LParen) {
2803                        let mut column_aliases = Vec::new();
2804                        loop {
2805                            if let Some(col_expr) = self.parse_id_var()? {
2806                                if let Expression::Identifier(id) = col_expr {
2807                                    column_aliases.push(id);
2808                                }
2809                            } else {
2810                                break;
2811                            }
2812                            if !self.match_token(TokenType::Comma) {
2813                                break;
2814                            }
2815                        }
2816                        self.match_token(TokenType::RParen);
2817                        let mut trailing_comments = as_comments;
2818                        trailing_comments.extend_from_slice(self.previous_trailing_comments());
2819                        Expression::Alias(Box::new(Alias {
2820                            this: expr,
2821                            alias: Identifier::new(String::new()),
2822                            column_aliases,
2823                            pre_alias_comments,
2824                            trailing_comments,
2825                            inferred_type: None,
2826                        }))
2827                    } else {
2828                        // Allow keywords as aliases (e.g., SELECT 1 AS filter)
2829                        // Use _with_quoted to preserve quoted alias
2830                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
2831                        let mut trailing_comments = self.previous_trailing_comments().to_vec();
2832                        // If parse_comparison stored pending leading comments (no comparison
2833                        // followed), use those. Otherwise use the leading_comments we captured
2834                        // before parse_expression(). Both come from the same token, so we
2835                        // only add one set to avoid duplication.
2836                        if !self.pending_leading_comments.is_empty() {
2837                            trailing_comments.extend(self.pending_leading_comments.drain(..));
2838                        } else {
2839                            trailing_comments.extend(leading_comments.iter().cloned());
2840                        }
2841                        Expression::Alias(Box::new(Alias {
2842                            this: expr,
2843                            alias,
2844                            column_aliases: Vec::new(),
2845                            pre_alias_comments,
2846                            trailing_comments,
2847                            inferred_type: None,
2848                        }))
2849                    }
2850                } else if ((self.check(TokenType::Var) && !self.check_keyword()) || self.check(TokenType::QuotedIdentifier) || self.can_be_alias_keyword() || self.is_command_keyword_as_alias() || self.check(TokenType::Overlaps)
2851                    // ClickHouse: APPLY without ( is an implicit alias (e.g., SELECT col apply)
2852                    || (self.check(TokenType::Apply) && !self.check_next(TokenType::LParen)
2853                        && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))))
2854                    && !self.check_text_seq(&["BULK", "COLLECT", "INTO"])
2855                    // ClickHouse clauses must not be consumed as implicit aliases.
2856                    && !(matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
2857                        && (self.check(TokenType::Format) || self.check(TokenType::Settings)))
2858                    // LIMIT/OFFSET/FETCH are clause starters in most dialects and must not
2859                    // be consumed as implicit aliases in SELECT lists.
2860                    && !(
2861                        self.check(TokenType::Fetch)
2862                        || ((self.check(TokenType::Limit) || self.check(TokenType::Offset))
2863                            && !matches!(
2864                                self.config.dialect,
2865                                Some(crate::dialects::DialectType::Spark)
2866                                    | Some(crate::dialects::DialectType::Hive)
2867                            ))
2868                    )
2869                    // GROUP BY / ORDER BY are clause boundaries, not aliases.
2870                    && !self.check_text_seq(&["GROUP", "BY"])
2871                    && !self.check_text_seq(&["ORDER", "BY"])
2872                    // WINDOW is a clause boundary (named window definitions), not an alias.
2873                    && !self.check(TokenType::Window)
2874                    // ClickHouse: PARALLEL WITH is a statement separator, not an alias.
2875                    && !(self.check_identifier("PARALLEL") && self.check_next(TokenType::With)
2876                        && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
2877                {
2878                    // Implicit alias (without AS) - allow Var tokens, QuotedIdentifiers, command keywords (like GET, PUT, etc.), and OVERLAPS
2879                    // But NOT when it's the Oracle BULK COLLECT INTO sequence
2880                    let alias_token = self.advance();
2881                    let alias_text = alias_token.text.clone();
2882                    let is_quoted = alias_token.token_type == TokenType::QuotedIdentifier;
2883                    let trailing_comments = self.previous_trailing_comments().to_vec();
2884                    Expression::Alias(Box::new(Alias {
2885                        this: expr,
2886                        alias: Identifier {
2887                            name: alias_text,
2888                            quoted: is_quoted,
2889                            trailing_comments: Vec::new(),
2890                            span: None,
2891                        },
2892                        column_aliases: Vec::new(),
2893                        pre_alias_comments,
2894                        trailing_comments,
2895                        inferred_type: None,
2896                    }))
2897                } else if !pre_alias_comments.is_empty() {
2898                    // Only wrap in Annotated if the expression doesn't already handle trailing comments.
2899                    // BinaryOp, Column, Cast, Function, etc. have their own trailing_comments field that the generator uses.
2900                    let already_has_trailing = matches!(
2901                        &expr,
2902                        Expression::Add(_)
2903                            | Expression::Sub(_)
2904                            | Expression::Mul(_)
2905                            | Expression::Div(_)
2906                            | Expression::Mod(_)
2907                            | Expression::Concat(_)
2908                            | Expression::BitwiseAnd(_)
2909                            | Expression::BitwiseOr(_)
2910                            | Expression::BitwiseXor(_)
2911                            | Expression::Column(_)
2912                            | Expression::Paren(_)
2913                            | Expression::Annotated(_)
2914                            | Expression::Cast(_)
2915                            | Expression::Function(_)
2916                            | Expression::Subquery(_)
2917                    );
2918                    if already_has_trailing {
2919                        expr
2920                    } else {
2921                        // Wrap in Annotated to preserve trailing comments
2922                        Expression::Annotated(Box::new(Annotated {
2923                            this: expr,
2924                            trailing_comments: pre_alias_comments,
2925                        }))
2926                    }
2927                } else if !leading_comments.is_empty() {
2928                    // Wrap in Annotated to preserve leading comments as trailing comments
2929                    Expression::Annotated(Box::new(Annotated {
2930                        this: expr,
2931                        trailing_comments: leading_comments,
2932                    }))
2933                } else {
2934                    expr
2935                };
2936
2937                expressions.push(expr);
2938            }
2939
2940            if !self.match_token(TokenType::Comma) {
2941                break;
2942            }
2943
2944            // Handle trailing comma (ClickHouse supports trailing commas in SELECT)
2945            // ClickHouse: `from` after comma is a column name if followed by an operator
2946            // (e.g., `from + from` or `from in [0]`), comma, or line-end
2947            let from_is_column = matches!(
2948                self.config.dialect,
2949                Some(crate::dialects::DialectType::ClickHouse)
2950            ) && self.check(TokenType::From)
2951                && {
2952                    let next_tt = self
2953                        .peek_nth(1)
2954                        .map(|t| t.token_type)
2955                        .unwrap_or(TokenType::Semicolon);
2956                    matches!(
2957                        next_tt,
2958                        TokenType::Plus
2959                            | TokenType::Dash
2960                            | TokenType::Star
2961                            | TokenType::Slash
2962                            | TokenType::Percent
2963                            | TokenType::Eq
2964                            | TokenType::Neq
2965                            | TokenType::Lt
2966                            | TokenType::Gt
2967                            | TokenType::Lte
2968                            | TokenType::Gte
2969                            | TokenType::And
2970                            | TokenType::Or
2971                            | TokenType::Comma
2972                            | TokenType::Dot
2973                            | TokenType::In
2974                            | TokenType::Is
2975                            | TokenType::Not
2976                            | TokenType::Like
2977                            | TokenType::Between
2978                            | TokenType::Semicolon
2979                            | TokenType::RParen
2980                            | TokenType::As
2981                            | TokenType::DPipe
2982                            | TokenType::Amp
2983                            | TokenType::Pipe
2984                            | TokenType::LBracket
2985                    )
2986                };
2987            if (self.config.allow_trailing_commas
2988                || matches!(
2989                    self.config.dialect,
2990                    Some(crate::dialects::DialectType::ClickHouse)
2991                ))
2992                && (!from_is_column && self.check_from_keyword()
2993                    || self.check(TokenType::Where)
2994                    || self.check(TokenType::GroupBy)
2995                    || self.check(TokenType::Having)
2996                    || self.check(TokenType::Order)
2997                    || self.check(TokenType::Limit)
2998                    || self.check(TokenType::Union)
2999                    || self.check(TokenType::Intersect)
3000                    || (self.check(TokenType::Except) && !self.check_next(TokenType::LParen) && !self.check_next(TokenType::Comma))
3001                    || self.check(TokenType::Semicolon)
3002                    || self.check(TokenType::RParen)
3003                    // SETTINGS/FORMAT only as boundaries when NOT followed by ( or [ (function/column ref)
3004                    || (self.check(TokenType::Settings) && !self.check_next(TokenType::LParen) && !self.check_next(TokenType::LBracket))
3005                    || (self.check(TokenType::Format) && !self.check_next(TokenType::LParen))
3006                    || self.is_at_end())
3007            {
3008                break;
3009            }
3010        }
3011
3012        Ok(expressions)
3013    }
3014
3015    /// Parse DuckDB FROM-first query syntax
3016    /// FROM tbl = SELECT * FROM tbl
3017    /// FROM tbl SELECT col1, col2 = SELECT col1, col2 FROM tbl
3018    fn parse_from_first_query(&mut self) -> Result<Expression> {
3019        self.expect(TokenType::From)?;
3020
3021        // Parse the FROM clause (table references)
3022        let from = self.parse_from()?;
3023
3024        // Check if there's an explicit SELECT clause after FROM
3025        let expressions = if self.check(TokenType::Select) {
3026            self.skip(); // consume SELECT
3027            self.parse_select_expressions()?
3028        } else {
3029            // No explicit SELECT means SELECT *
3030            vec![Expression::Star(crate::expressions::Star {
3031                table: None,
3032                except: None,
3033                replace: None,
3034                rename: None,
3035                trailing_comments: Vec::new(),
3036                span: None,
3037            })]
3038        };
3039
3040        // Parse PREWHERE clause (ClickHouse specific)
3041        let prewhere = if self.match_token(TokenType::Prewhere) {
3042            Some(self.parse_expression()?)
3043        } else {
3044            None
3045        };
3046
3047        // Parse WHERE clause
3048        let where_clause = if self.match_token(TokenType::Where) {
3049            Some(Where {
3050                this: self.parse_expression()?,
3051            })
3052        } else {
3053            None
3054        };
3055
3056        // Parse GROUP BY
3057        let group_by = if self.match_token(TokenType::Group) {
3058            self.expect(TokenType::By)?;
3059            let mut groups = Vec::new();
3060            loop {
3061                groups.push(self.parse_expression()?);
3062                if !self.match_token(TokenType::Comma) {
3063                    break;
3064                }
3065            }
3066            Some(GroupBy {
3067                expressions: groups,
3068                all: None,
3069                totals: false,
3070                comments: Vec::new(),
3071            })
3072        } else {
3073            None
3074        };
3075
3076        // Parse HAVING
3077        let having = if self.match_token(TokenType::Having) {
3078            Some(Having {
3079                this: self.parse_expression()?,
3080                comments: Vec::new(),
3081            })
3082        } else {
3083            None
3084        };
3085
3086        // Parse ORDER BY
3087        let order_by = if self.match_token(TokenType::Order) {
3088            self.expect(TokenType::By)?;
3089            Some(self.parse_order_by()?)
3090        } else {
3091            None
3092        };
3093
3094        // Parse LIMIT
3095        let limit = if self.match_token(TokenType::Limit) {
3096            let first_expr = self.parse_expression()?;
3097            Some(Limit {
3098                this: first_expr,
3099                percent: false,
3100                comments: Vec::new(),
3101            })
3102        } else {
3103            None
3104        };
3105
3106        // Parse OFFSET
3107        let offset = if self.match_token(TokenType::Offset) {
3108            let expr = self.parse_expression()?;
3109            let rows = if self.match_token(TokenType::Row) || self.match_token(TokenType::Rows) {
3110                Some(true)
3111            } else {
3112                None
3113            };
3114            Some(Offset { this: expr, rows })
3115        } else {
3116            None
3117        };
3118
3119        // Build SELECT expression
3120        let select = Select {
3121            expressions,
3122            from: Some(from),
3123            joins: Vec::new(),
3124            lateral_views: Vec::new(),
3125            prewhere,
3126            where_clause,
3127            group_by,
3128            having,
3129            qualify: None,
3130            order_by,
3131            distribute_by: None,
3132            cluster_by: None,
3133            sort_by: None,
3134            limit,
3135            offset,
3136            limit_by: None,
3137            fetch: None,
3138            distinct: false,
3139            distinct_on: None,
3140            top: None,
3141            with: None,
3142            sample: None,
3143            settings: None,
3144            format: None,
3145            windows: None,
3146            hint: None,
3147            connect: None,
3148            into: None,
3149            locks: Vec::new(),
3150            for_xml: Vec::new(),
3151            leading_comments: Vec::new(),
3152            post_select_comments: Vec::new(),
3153            kind: None,
3154            operation_modifiers: Vec::new(),
3155            qualify_after_window: false,
3156            option: None,
3157            exclude: None,
3158        };
3159
3160        // Check for set operations (UNION, INTERSECT, EXCEPT)
3161        let result = Expression::Select(Box::new(select));
3162        self.parse_set_operation(result)
3163    }
3164
3165    /// Parse FROM clause
3166    fn parse_from(&mut self) -> Result<From> {
3167        let mut expressions = Vec::new();
3168
3169        loop {
3170            // Capture leading comments before each table expression
3171            // (e.g., FROM \n/* comment */\n table_name)
3172            let pre_table_comments = if !self.is_at_end() {
3173                self.tokens[self.current].comments.clone()
3174            } else {
3175                Vec::new()
3176            };
3177            // Clear them from the token to avoid double output
3178            if !pre_table_comments.is_empty() && !self.is_at_end() {
3179                self.tokens[self.current].comments.clear();
3180            }
3181
3182            let mut table = self.parse_table_expression()?;
3183
3184            // Attach captured comments as trailing on the outermost expression
3185            if !pre_table_comments.is_empty() {
3186                match &mut table {
3187                    Expression::Pivot(p) => {
3188                        // For PIVOT, find the inner table and add to its leading_comments
3189                        // The generator will output these after the PIVOT clause
3190                        if let Expression::Table(ref mut t) = p.this {
3191                            t.leading_comments = pre_table_comments;
3192                        }
3193                    }
3194                    Expression::Table(ref mut t) => {
3195                        t.trailing_comments.extend(pre_table_comments);
3196                    }
3197                    _ => {}
3198                }
3199            }
3200            expressions.push(table);
3201
3202            if !self.match_token(TokenType::Comma) {
3203                break;
3204            }
3205
3206            // Handle trailing comma in FROM clause (Snowflake allows this)
3207            // If next token is a clause boundary keyword or end of input, break
3208            // Note: For Redshift, UNPIVOT after comma is a table expression (SUPER object traversal),
3209            // so we don't treat it as a boundary in that case
3210            let is_redshift = matches!(
3211                self.config.dialect,
3212                Some(crate::dialects::DialectType::Redshift)
3213            );
3214            let is_unpivot_boundary = !is_redshift && self.check(TokenType::Unpivot);
3215            if self.is_at_end()
3216                || is_unpivot_boundary
3217                || matches!(
3218                    self.peek().token_type,
3219                    TokenType::Where
3220                        | TokenType::GroupBy
3221                        | TokenType::Having
3222                        | TokenType::Order
3223                        | TokenType::Limit
3224                        | TokenType::Offset
3225                        | TokenType::Union
3226                        | TokenType::Intersect
3227                        | TokenType::Except
3228                        | TokenType::Semicolon
3229                        | TokenType::RParen
3230                        | TokenType::Window
3231                        | TokenType::Qualify
3232                        | TokenType::Distribute
3233                        | TokenType::Cluster
3234                        | TokenType::Pivot
3235                )
3236            {
3237                break;
3238            }
3239        }
3240
3241        Ok(From { expressions })
3242    }
3243
3244    /// Parse a table expression (table name, subquery, etc.)
3245    fn parse_table_expression(&mut self) -> Result<Expression> {
3246        // Handle PostgreSQL ONLY modifier: FROM ONLY t1
3247        // ONLY prevents scanning child tables in inheritance hierarchy
3248        let has_only = self.match_token(TokenType::Only);
3249
3250        // Handle PostgreSQL ROWS FROM syntax:
3251        // ROWS FROM (func1(args) AS alias1(col1 type1), func2(args) AS alias2(col2 type2)) [WITH ORDINALITY] [AS alias(cols)]
3252        if self.match_text_seq(&["ROWS", "FROM"]) {
3253            return self.parse_rows_from();
3254        }
3255
3256        // Redshift UNPIVOT in FROM clause for SUPER object traversal:
3257        // UNPIVOT expr [AS val_alias AT attr_alias]
3258        // Examples:
3259        //   UNPIVOT c.c_orders[0]
3260        //   UNPIVOT c.c_orders AS val AT attr
3261        if self.match_token(TokenType::Unpivot) {
3262            return self.parse_redshift_unpivot_table();
3263        }
3264
3265        let mut expr = if self.check(TokenType::Values) && self.check_next(TokenType::LParen) {
3266            // VALUES as table expression: FROM (VALUES ...)
3267            // In ClickHouse, bare `values` without ( is a table name
3268            self.parse_values()?
3269        } else if self.check(TokenType::Values)
3270            && matches!(
3271                self.config.dialect,
3272                Some(crate::dialects::DialectType::ClickHouse)
3273            )
3274        {
3275            // ClickHouse: `values` as a table name (not followed by LParen)
3276            let token = self.advance();
3277            let ident = Identifier::new(token.text);
3278            let trailing_comments = self.previous_trailing_comments().to_vec();
3279            Expression::boxed_table(TableRef {
3280                name: ident,
3281                schema: None,
3282                catalog: None,
3283                alias: None,
3284                alias_explicit_as: false,
3285                column_aliases: Vec::new(),
3286                leading_comments: Vec::new(),
3287                trailing_comments,
3288                when: None,
3289                only: false,
3290                final_: false,
3291                table_sample: None,
3292                hints: Vec::new(),
3293                system_time: None,
3294                partitions: Vec::new(),
3295                identifier_func: None,
3296                changes: None,
3297                version: None,
3298                span: None,
3299            })
3300        } else if self.check(TokenType::DAt) {
3301            // Snowflake stage reference: @stage_name or @"stage_name" or @namespace.stage/path
3302            self.parse_stage_reference()?
3303        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
3304            // Snowflake stage reference tokenized as Var: @mystage/path
3305            // When @ is followed by alphanumeric, tokenizer creates a Var token instead of DAt
3306            self.parse_stage_reference_from_var()?
3307        } else if self.check(TokenType::String) && self.peek().text.starts_with('@') {
3308            // Snowflake stage reference in string: '@mystage' or '@external/location'
3309            self.parse_stage_reference_from_string()?
3310        } else if self.match_token(TokenType::Lateral) {
3311            if self.check(TokenType::LParen) {
3312                // LATERAL (SELECT ...) or LATERAL (table_expression) or LATERAL (FROM ...) for DuckDB
3313                self.expect(TokenType::LParen)?;
3314                if self.check(TokenType::Select)
3315                    || self.check(TokenType::With)
3316                    || self.check(TokenType::From)
3317                {
3318                    let query = self.parse_statement()?;
3319                    self.expect(TokenType::RParen)?;
3320                    Expression::Subquery(Box::new(Subquery {
3321                        this: query,
3322                        alias: None,
3323                        column_aliases: Vec::new(),
3324                        order_by: None,
3325                        limit: None,
3326                        offset: None,
3327                        lateral: true,
3328                        modifiers_inside: false,
3329                        trailing_comments: Vec::new(),
3330                        distribute_by: None,
3331                        sort_by: None,
3332                        cluster_by: None,
3333                        inferred_type: None,
3334                    }))
3335                } else {
3336                    // LATERAL (table_function()) - parenthesized non-subquery
3337                    let table_expr = self.parse_table_expression()?;
3338                    self.expect(TokenType::RParen)?;
3339                    Expression::Subquery(Box::new(Subquery {
3340                        this: table_expr,
3341                        alias: None,
3342                        column_aliases: Vec::new(),
3343                        order_by: None,
3344                        limit: None,
3345                        offset: None,
3346                        lateral: true,
3347                        modifiers_inside: false,
3348                        trailing_comments: Vec::new(),
3349                        distribute_by: None,
3350                        sort_by: None,
3351                        cluster_by: None,
3352                        inferred_type: None,
3353                    }))
3354                }
3355            } else {
3356                // LATERAL function_name(args) [WITH ORDINALITY] [AS alias(columns)]
3357                // Parse function name
3358                let first_ident = self.expect_identifier_or_keyword_with_quoted()?;
3359                let first_name = first_ident.name.clone();
3360
3361                // Parse function arguments
3362                self.expect(TokenType::LParen)?;
3363                let args = if self.check(TokenType::RParen) {
3364                    Vec::new()
3365                } else {
3366                    self.parse_function_arguments()?
3367                };
3368                self.expect(TokenType::RParen)?;
3369
3370                // Handle UNNEST specially to create UnnestFunc expression
3371                let mut func_expr = if first_name.eq_ignore_ascii_case("UNNEST") {
3372                    let mut args_iter = args.into_iter();
3373                    let this = args_iter
3374                        .next()
3375                        .ok_or_else(|| self.parse_error("Expected expression in UNNEST"))?;
3376                    let expressions: Vec<Expression> = args_iter.collect();
3377                    Expression::Unnest(Box::new(crate::expressions::UnnestFunc {
3378                        this,
3379                        expressions,
3380                        with_ordinality: false,
3381                        alias: None,
3382                        offset_alias: None,
3383                    }))
3384                } else {
3385                    Expression::Function(Box::new(Function {
3386                        name: first_name,
3387                        args,
3388                        distinct: false,
3389                        trailing_comments: Vec::new(),
3390                        use_bracket_syntax: false,
3391                        no_parens: false,
3392                        quoted: false,
3393                        span: None,
3394                        inferred_type: None,
3395                    }))
3396                };
3397
3398                // Check for WITH ORDINALITY (Presto) or WITH OFFSET (BigQuery)
3399                let mut with_offset_alias: Option<crate::expressions::Identifier> = None;
3400                let ordinality = if self.match_token(TokenType::With) {
3401                    if self.match_token(TokenType::Ordinality) {
3402                        Some(Box::new(Expression::Boolean(BooleanLiteral {
3403                            value: true,
3404                        })))
3405                    } else if self.check(TokenType::Offset) || self.check_identifier("OFFSET") {
3406                        // BigQuery: WITH OFFSET [AS alias]
3407                        self.skip(); // consume OFFSET
3408                                     // Check for optional offset alias: WITH OFFSET AS y or WITH OFFSET y
3409                        if matches!(
3410                            self.config.dialect,
3411                            Some(crate::dialects::DialectType::BigQuery)
3412                        ) {
3413                            let has_as = self.match_token(TokenType::As);
3414                            if has_as
3415                                || self.check(TokenType::Identifier)
3416                                || self.check(TokenType::Var)
3417                            {
3418                                let alias_name = self.advance().text;
3419                                with_offset_alias = Some(crate::expressions::Identifier {
3420                                    name: alias_name,
3421                                    quoted: false,
3422                                    trailing_comments: Vec::new(),
3423                                    span: None,
3424                                });
3425                            }
3426                        }
3427                        Some(Box::new(Expression::Boolean(BooleanLiteral {
3428                            value: true,
3429                        })))
3430                    } else {
3431                        // Not ORDINALITY or OFFSET, put back WITH
3432                        self.current -= 1;
3433                        None
3434                    }
3435                } else {
3436                    None
3437                };
3438
3439                // Update the inner UnnestFunc with WITH ORDINALITY/OFFSET info
3440                if ordinality.is_some() {
3441                    if let Expression::Unnest(ref mut u) = func_expr {
3442                        u.with_ordinality = true;
3443                        u.offset_alias = with_offset_alias;
3444                    }
3445                }
3446
3447                // Parse optional alias: AS alias or just alias
3448                let alias_ident = if self.match_token(TokenType::As) {
3449                    Some(self.expect_identifier_or_keyword_with_quoted()?)
3450                } else if !self.is_at_end()
3451                    && !self.check(TokenType::Comma)
3452                    && !self.check(TokenType::RParen)
3453                    && !self.check(TokenType::On)
3454                    && !self.check(TokenType::Cross)
3455                    && !self.check(TokenType::Inner)
3456                    && !self.check(TokenType::Left)
3457                    && !self.check(TokenType::Right)
3458                    && !self.check(TokenType::Full)
3459                    && !self.check(TokenType::Join)
3460                    && !self.check(TokenType::Where)
3461                    && !self.check(TokenType::Order)
3462                    && !self.check(TokenType::Limit)
3463                    && !self.check(TokenType::Semicolon)
3464                    && (self.check(TokenType::Identifier) || self.check(TokenType::Var))
3465                {
3466                    Some(self.expect_identifier_or_keyword_with_quoted()?)
3467                } else {
3468                    None
3469                };
3470                let alias_quoted = alias_ident.as_ref().map_or(false, |id| id.quoted);
3471                let alias = alias_ident.map(|id| id.name);
3472
3473                // Parse column aliases: (col1, col2, ...)
3474                let column_aliases = if alias.is_some() && self.match_token(TokenType::LParen) {
3475                    let mut cols = Vec::new();
3476                    loop {
3477                        cols.push(self.expect_identifier_or_keyword()?);
3478                        if !self.match_token(TokenType::Comma) {
3479                            break;
3480                        }
3481                    }
3482                    self.expect(TokenType::RParen)?;
3483                    cols
3484                } else {
3485                    Vec::new()
3486                };
3487
3488                Expression::Lateral(Box::new(Lateral {
3489                    this: Box::new(func_expr),
3490                    view: None,
3491                    outer: None,
3492                    alias,
3493                    alias_quoted,
3494                    cross_apply: None,
3495                    ordinality,
3496                    column_aliases,
3497                }))
3498            }
3499        } else if self.match_token(TokenType::LParen) {
3500            // Subquery or parenthesized set operation or (VALUES ...)
3501            if self.check(TokenType::Values) {
3502                // (VALUES (...), (...)) AS t(c1, c2) or (VALUES (0) foo(bar))
3503                let mut values = self.parse_values()?;
3504                self.expect(TokenType::RParen)?;
3505                // Extract alias from Values if present and move to Subquery
3506                let (alias, column_aliases) = if let Expression::Values(ref mut v) = values {
3507                    (v.alias.take(), std::mem::take(&mut v.column_aliases))
3508                } else {
3509                    (None, Vec::new())
3510                };
3511                Expression::Subquery(Box::new(Subquery {
3512                    this: values,
3513                    alias,
3514                    column_aliases,
3515                    order_by: None,
3516                    limit: None,
3517                    offset: None,
3518                    distribute_by: None,
3519                    sort_by: None,
3520                    cluster_by: None,
3521                    lateral: false,
3522                    modifiers_inside: false,
3523                    trailing_comments: self.previous_trailing_comments().to_vec(),
3524                    inferred_type: None,
3525                }))
3526            } else if self.check(TokenType::Select)
3527                || self.check(TokenType::With)
3528                || self.check(TokenType::Pivot)
3529                || self.check(TokenType::Unpivot)
3530                || self.check(TokenType::From)
3531                || self.check(TokenType::Merge)
3532                || self.check(TokenType::Describe)
3533                || (self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EXPLAIN"))
3534                || (self.check(TokenType::Var)
3535                    && self.peek().text.eq_ignore_ascii_case("SUMMARIZE"))
3536            {
3537                let query = self.parse_statement()?;
3538                self.expect(TokenType::RParen)?;
3539                let trailing = self.previous_trailing_comments().to_vec();
3540                // Check for set operations after parenthesized query
3541                // If there's a set operation, wrap query in Subquery first to preserve parens
3542                // e.g., (SELECT 1) UNION (SELECT 2) - the left operand needs Subquery wrapper
3543                let result = if self.check(TokenType::Union)
3544                    || self.check(TokenType::Intersect)
3545                    || self.check(TokenType::Except)
3546                {
3547                    let left = Expression::Subquery(Box::new(Subquery {
3548                        this: query,
3549                        alias: None,
3550                        column_aliases: Vec::new(),
3551                        order_by: None,
3552                        limit: None,
3553                        offset: None,
3554                        lateral: false,
3555                        modifiers_inside: false,
3556                        trailing_comments: Vec::new(),
3557                        distribute_by: None,
3558                        sort_by: None,
3559                        cluster_by: None,
3560                        inferred_type: None,
3561                    }));
3562                    self.parse_set_operation(left)?
3563                } else {
3564                    query
3565                };
3566                Expression::Subquery(Box::new(Subquery {
3567                    this: result,
3568                    alias: None,
3569                    column_aliases: Vec::new(),
3570                    order_by: None,
3571                    limit: None,
3572                    offset: None,
3573                    distribute_by: None,
3574                    sort_by: None,
3575                    cluster_by: None,
3576                    lateral: false,
3577                    modifiers_inside: false,
3578                    trailing_comments: trailing,
3579                    inferred_type: None,
3580                }))
3581            } else if self.check(TokenType::LParen) {
3582                // Nested parens like ((SELECT ...)) or ((x))
3583                // Also handles ((SELECT 1) UNION (SELECT 2)) - set operations inside parens
3584                let inner = self.parse_table_expression()?;
3585
3586                // Handle alias on subquery before set operation: ((SELECT 1) AS a UNION ALL (SELECT 2) AS b)
3587                let inner = if self.match_token(TokenType::As) {
3588                    let alias = self.expect_identifier()?;
3589                    if let Expression::Subquery(mut subq) = inner {
3590                        subq.alias = Some(Identifier::new(alias));
3591                        Expression::Subquery(subq)
3592                    } else {
3593                        Expression::Alias(Box::new(Alias::new(inner, Identifier::new(alias))))
3594                    }
3595                } else if self.is_identifier_token()
3596                    && !self.check(TokenType::Union)
3597                    && !self.check(TokenType::Intersect)
3598                    && !self.check(TokenType::Except)
3599                    && !self.check(TokenType::Cross)
3600                    && !self.check(TokenType::Inner)
3601                    && !self.check(TokenType::Left)
3602                    && !self.check(TokenType::Right)
3603                    && !self.check(TokenType::Full)
3604                    && !self.check(TokenType::Join)
3605                    && !self.check(TokenType::Order)
3606                    && !self.check(TokenType::Limit)
3607                    && !self.check(TokenType::Offset)
3608                    && !self.check(TokenType::Xor)
3609                {
3610                    // Implicit alias (no AS keyword)
3611                    let alias = self.expect_identifier()?;
3612                    if let Expression::Subquery(mut subq) = inner {
3613                        subq.alias = Some(Identifier::new(alias));
3614                        Expression::Subquery(subq)
3615                    } else {
3616                        Expression::Alias(Box::new(Alias::new(inner, Identifier::new(alias))))
3617                    }
3618                } else {
3619                    inner
3620                };
3621
3622                // ClickHouse: ((SELECT 1) AS x, (SELECT 2) AS y) — tuple of aliased subqueries
3623                if matches!(
3624                    self.config.dialect,
3625                    Some(crate::dialects::DialectType::ClickHouse)
3626                ) && self.check(TokenType::Comma)
3627                {
3628                    let mut exprs = vec![inner];
3629                    while self.match_token(TokenType::Comma) {
3630                        if self.check(TokenType::RParen) {
3631                            break;
3632                        }
3633                        let e = self.parse_expression()?;
3634                        exprs.push(e);
3635                    }
3636                    self.expect(TokenType::RParen)?;
3637                    return Ok(Expression::Tuple(Box::new(Tuple { expressions: exprs })));
3638                }
3639
3640                // Check for set operations after the first table expression
3641                let had_set_operation = self.check(TokenType::Union)
3642                    || self.check(TokenType::Intersect)
3643                    || self.check(TokenType::Except);
3644                let result = if had_set_operation {
3645                    // This is a set operation like ((SELECT 1) UNION (SELECT 2))
3646                    // Wrap inner in a subquery-like expression and parse set operation
3647                    let set_result = self.parse_set_operation(inner)?;
3648                    set_result
3649                } else if self.check(TokenType::Cross)
3650                    || self.check(TokenType::Inner)
3651                    || self.check(TokenType::Left)
3652                    || self.check(TokenType::Right)
3653                    || self.check(TokenType::Full)
3654                    || self.check(TokenType::Join)
3655                {
3656                    // This is a join: ((SELECT 1) CROSS JOIN (SELECT 2))
3657                    let joins = self.parse_joins()?;
3658                    let lateral_views = self.parse_lateral_views()?;
3659                    Expression::JoinedTable(Box::new(JoinedTable {
3660                        left: inner,
3661                        joins,
3662                        lateral_views,
3663                        alias: None,
3664                    }))
3665                } else {
3666                    inner
3667                };
3668
3669                // Handle ORDER BY, LIMIT, OFFSET after set operations inside parens
3670                let result = if self.check(TokenType::Order) {
3671                    // Wrap in a subquery with order/limit
3672                    self.expect(TokenType::Order)?;
3673                    self.expect(TokenType::By)?;
3674                    let order_by = self.parse_order_by()?;
3675                    let limit = if self.match_token(TokenType::Limit) {
3676                        Some(Limit {
3677                            this: self.parse_expression()?,
3678                            percent: false,
3679                            comments: Vec::new(),
3680                        })
3681                    } else {
3682                        None
3683                    };
3684                    let offset = if self.match_token(TokenType::Offset) {
3685                        Some(Offset {
3686                            this: self.parse_expression()?,
3687                            rows: None,
3688                        })
3689                    } else {
3690                        None
3691                    };
3692                    Expression::Subquery(Box::new(Subquery {
3693                        this: result,
3694                        alias: None,
3695                        column_aliases: Vec::new(),
3696                        order_by: Some(order_by),
3697                        limit,
3698                        offset,
3699                        distribute_by: None,
3700                        sort_by: None,
3701                        cluster_by: None,
3702                        lateral: false,
3703                        modifiers_inside: true, // ORDER BY was inside the parens
3704                        trailing_comments: Vec::new(),
3705                        inferred_type: None,
3706                    }))
3707                } else if self.check(TokenType::Limit) || self.check(TokenType::Offset) {
3708                    // LIMIT/OFFSET without ORDER BY
3709                    let limit = if self.match_token(TokenType::Limit) {
3710                        Some(Limit {
3711                            this: self.parse_expression()?,
3712                            percent: false,
3713                            comments: Vec::new(),
3714                        })
3715                    } else {
3716                        None
3717                    };
3718                    let offset = if self.match_token(TokenType::Offset) {
3719                        Some(Offset {
3720                            this: self.parse_expression()?,
3721                            rows: None,
3722                        })
3723                    } else {
3724                        None
3725                    };
3726                    Expression::Subquery(Box::new(Subquery {
3727                        this: result,
3728                        alias: None,
3729                        column_aliases: Vec::new(),
3730                        order_by: None,
3731                        limit,
3732                        offset,
3733                        distribute_by: None,
3734                        sort_by: None,
3735                        cluster_by: None,
3736                        lateral: false,
3737                        modifiers_inside: true, // LIMIT/OFFSET was inside the parens
3738                        trailing_comments: Vec::new(),
3739                        inferred_type: None,
3740                    }))
3741                } else {
3742                    result
3743                };
3744
3745                self.expect(TokenType::RParen)?;
3746                // Wrap result in Paren to preserve the outer parentheses when needed
3747                // Cases:
3748                // - ((SELECT 1)) -> Paren(Subquery(Select)) - inner was subquery of SELECT, wrap in Paren
3749                // - ((SELECT 1) UNION (SELECT 2)) -> Subquery(Union) - recursive call handled set op, don't add Paren
3750                // - ((SELECT 1) AS a UNION ALL ...) -> Union - we handled set op, need to add Paren
3751                // - (((SELECT 1) UNION SELECT 2) ORDER BY x) -> Subquery with modifiers_inside=true
3752                let had_modifiers = matches!(&result, Expression::Subquery(s) if s.order_by.is_some() || s.limit.is_some() || s.offset.is_some());
3753                let result_is_subquery_of_set_op = matches!(&result, Expression::Subquery(s) if matches!(&s.this, Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)));
3754                if had_modifiers || result_is_subquery_of_set_op {
3755                    // Subquery with modifiers or Subquery(Union) - already has proper structure
3756                    result
3757                } else {
3758                    // All other cases need Paren wrapper to preserve outer parentheses
3759                    Expression::Paren(Box::new(Paren {
3760                        this: result,
3761                        trailing_comments: Vec::new(),
3762                    }))
3763                }
3764            } else if self.is_identifier_token()
3765                || self.is_safe_keyword_as_identifier()
3766                || self.can_be_alias_keyword()
3767            {
3768                // Parenthesized join expression: (tbl1 CROSS JOIN tbl2) or just (x)
3769                // Also allow safe keywords and alias keywords (all, left, etc.) as table names
3770                let (left, joins) = self.parse_table_expression_with_joins()?;
3771                // Parse LATERAL VIEW after joins: (x CROSS JOIN foo LATERAL VIEW EXPLODE(y))
3772                let lateral_views = self.parse_lateral_views()?;
3773                self.expect(TokenType::RParen)?;
3774                if joins.is_empty() && lateral_views.is_empty() {
3775                    // Just a parenthesized table expression, wrap in Paren to preserve parens
3776                    Expression::Paren(Box::new(Paren {
3777                        this: left,
3778                        trailing_comments: Vec::new(),
3779                    }))
3780                } else {
3781                    // Create a JoinedTable
3782                    Expression::JoinedTable(Box::new(JoinedTable {
3783                        left,
3784                        joins,
3785                        lateral_views,
3786                        alias: None, // Alias is parsed separately after this
3787                    }))
3788                }
3789            } else {
3790                let query = self.parse_statement()?;
3791                self.expect(TokenType::RParen)?;
3792                Expression::Subquery(Box::new(Subquery {
3793                    this: query,
3794                    alias: None,
3795                    column_aliases: Vec::new(),
3796                    order_by: None,
3797                    limit: None,
3798                    offset: None,
3799                    distribute_by: None,
3800                    sort_by: None,
3801                    cluster_by: None,
3802                    lateral: false,
3803                    modifiers_inside: false,
3804                    trailing_comments: self.previous_trailing_comments().to_vec(),
3805                    inferred_type: None,
3806                }))
3807            }
3808        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() || self.can_be_alias_keyword()
3809            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) && self.check(TokenType::Number))
3810            || self.is_mysql_numeric_identifier()
3811            // PIVOT/UNPIVOT can be table names when not followed by (
3812            || (self.check(TokenType::Pivot) && !self.check_next(TokenType::LParen))
3813            || (self.check(TokenType::Unpivot) && !self.check_next(TokenType::LParen))
3814            // ClickHouse: braced query parameters as table names {db:Identifier}.table
3815            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) && self.check(TokenType::LBrace))
3816            // ClickHouse: allow union/except/intersect as table names when not followed by ALL/DISTINCT/SELECT/(
3817            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
3818                && (self.check(TokenType::Union) || self.check(TokenType::Except) || self.check(TokenType::Intersect))
3819                && !self.check_next(TokenType::All) && !self.check_next(TokenType::Distinct)
3820                && !self.check_next(TokenType::Select) && !self.check_next(TokenType::LParen))
3821        {
3822            // Table name - could be simple, qualified, or table function
3823            // Also allow safe keywords (like 'table', 'view', 'case', 'all', etc.) as table names
3824            // BigQuery: also allows numeric table parts and hyphenated identifiers
3825            // MySQL: allows numeric-starting identifiers (e.g., 00f, 1d)
3826
3827            // DuckDB prefix alias syntax: alias: table (e.g., "foo: bar" means "bar AS foo")
3828            // Check if next token is COLON (but not :: which is DCOLON for casts)
3829            if matches!(
3830                self.config.dialect,
3831                Some(crate::dialects::DialectType::DuckDB)
3832            ) && self.check_next(TokenType::Colon)
3833                && !(self.current + 2 < self.tokens.len()
3834                    && self.tokens[self.current + 2].token_type == TokenType::Colon)
3835            {
3836                // Parse the alias identifier
3837                let alias_ident = self.parse_bigquery_table_part()?;
3838                let pre_alias_comments = self.previous_trailing_comments().to_vec();
3839                // Consume the colon
3840                self.expect(TokenType::Colon)?;
3841                let colon_comments = self.previous_trailing_comments().to_vec();
3842                // Parse the actual table expression recursively
3843                let mut table_expr = self.parse_table_expression()?;
3844                // Merge comments
3845                let mut all_comments = pre_alias_comments;
3846                all_comments.extend(colon_comments);
3847                // Apply the alias to the table expression
3848                match &mut table_expr {
3849                    Expression::Table(ref mut t) => {
3850                        t.alias = Some(alias_ident);
3851                        t.alias_explicit_as = true; // Output AS keyword (required by expected format)
3852                                                    // Store prefix alias comments - they should come BEFORE the table's trailing comments
3853                                                    // For "foo /* bla */: bar /* baz */", output is "bar AS foo /* bla */ /* baz */"
3854                                                    // So alias comments (/* bla */) come first, then table comments (/* baz */)
3855                        if !all_comments.is_empty() {
3856                            let existing_comments = std::mem::take(&mut t.trailing_comments);
3857                            t.trailing_comments = all_comments;
3858                            t.trailing_comments.extend(existing_comments);
3859                        }
3860                    }
3861                    Expression::Subquery(ref mut s) => {
3862                        s.alias = Some(alias_ident);
3863                    }
3864                    Expression::Function(ref mut _f) => {
3865                        // Wrap function in alias
3866                        return Ok(Expression::Alias(Box::new(Alias {
3867                            this: table_expr,
3868                            alias: alias_ident,
3869                            column_aliases: Vec::new(),
3870                            pre_alias_comments: all_comments,
3871                            trailing_comments: Vec::new(),
3872                            inferred_type: None,
3873                        })));
3874                    }
3875                    _ => {
3876                        // For other expressions, wrap in Alias
3877                        return Ok(Expression::Alias(Box::new(Alias {
3878                            this: table_expr,
3879                            alias: alias_ident,
3880                            column_aliases: Vec::new(),
3881                            pre_alias_comments: all_comments,
3882                            trailing_comments: Vec::new(),
3883                            inferred_type: None,
3884                        })));
3885                    }
3886                }
3887                return Ok(table_expr);
3888            }
3889
3890            let first_ident = self.parse_bigquery_table_part()?;
3891            let first_name = first_ident.name.clone();
3892
3893            // Check for qualified name (schema.table) or table function
3894            if self.match_token(TokenType::Dot) {
3895                // Handle TSQL a..b syntax (database..table with empty schema)
3896                if self.check(TokenType::Dot) {
3897                    // Two consecutive dots: a..b means catalog..table (empty schema)
3898                    self.skip(); // consume second dot
3899                    let table_ident = self.parse_bigquery_table_part()?;
3900                    let trailing_comments = self.previous_trailing_comments().to_vec();
3901                    return Ok(Expression::boxed_table(TableRef {
3902                        catalog: Some(first_ident),
3903                        schema: Some(Identifier::new("")), // Empty schema represents ..
3904                        name: table_ident,
3905                        alias: None,
3906                        alias_explicit_as: false,
3907                        column_aliases: Vec::new(),
3908                        leading_comments: Vec::new(),
3909                        trailing_comments,
3910                        when: None,
3911                        only: false,
3912                        final_: false,
3913                        table_sample: None,
3914                        hints: Vec::new(),
3915                        system_time: None,
3916                        partitions: Vec::new(),
3917                        identifier_func: None,
3918                        changes: None,
3919                        version: None,
3920                        span: None,
3921                    }));
3922                }
3923
3924                // BigQuery: handle x.* wildcard table reference (e.g., SELECT * FROM x.*)
3925                // After the first dot, if we see a Star token, it's a wildcard table name
3926                if matches!(
3927                    self.config.dialect,
3928                    Some(crate::dialects::DialectType::BigQuery)
3929                ) && self.check(TokenType::Star)
3930                {
3931                    self.skip(); // consume *
3932                    let trailing_comments = self.previous_trailing_comments().to_vec();
3933                    return Ok(Expression::boxed_table(TableRef {
3934                        catalog: None,
3935                        schema: Some(first_ident),
3936                        name: Identifier::new("*"),
3937                        alias: None,
3938                        alias_explicit_as: false,
3939                        column_aliases: Vec::new(),
3940                        leading_comments: Vec::new(),
3941                        trailing_comments,
3942                        when: None,
3943                        only: false,
3944                        final_: false,
3945                        table_sample: None,
3946                        hints: Vec::new(),
3947                        system_time: None,
3948                        partitions: Vec::new(),
3949                        identifier_func: None,
3950                        changes: None,
3951                        version: None,
3952                        span: None,
3953                    }));
3954                }
3955
3956                // schema.table or schema.function()
3957                // Allow keywords as table/schema names (e.g., schema.table, catalog.view)
3958                let second_ident = self.parse_bigquery_table_part()?;
3959                let second_name = second_ident.name.clone();
3960
3961                if self.match_token(TokenType::Dot) {
3962                    // BigQuery: handle a.b.* wildcard table reference
3963                    if matches!(
3964                        self.config.dialect,
3965                        Some(crate::dialects::DialectType::BigQuery)
3966                    ) && self.check(TokenType::Star)
3967                    {
3968                        self.skip(); // consume *
3969                        let trailing_comments = self.previous_trailing_comments().to_vec();
3970                        return Ok(Expression::boxed_table(TableRef {
3971                            catalog: Some(first_ident),
3972                            schema: Some(second_ident),
3973                            name: Identifier::new("*"),
3974                            alias: None,
3975                            alias_explicit_as: false,
3976                            column_aliases: Vec::new(),
3977                            leading_comments: Vec::new(),
3978                            trailing_comments,
3979                            when: None,
3980                            only: false,
3981                            final_: false,
3982                            table_sample: None,
3983                            hints: Vec::new(),
3984                            system_time: None,
3985                            partitions: Vec::new(),
3986                            identifier_func: None,
3987                            changes: None,
3988                            version: None,
3989                            span: None,
3990                        }));
3991                    }
3992                    // catalog.schema.table or catalog.schema.function()
3993                    let third_ident = self.parse_bigquery_table_part()?;
3994                    let third_name = third_ident.name.clone();
3995
3996                    // Check for 4-part name (e.g., project.dataset.INFORMATION_SCHEMA.TABLES)
3997                    if self.match_token(TokenType::Dot) {
3998                        let fourth_ident = self.parse_bigquery_table_part()?;
3999                        // BigQuery wildcard table suffix: a.b.c.d* matches all tables starting with d
4000                        let mut table_name = fourth_ident;
4001                        if matches!(
4002                            self.config.dialect,
4003                            Some(crate::dialects::DialectType::BigQuery)
4004                        ) && self.check(TokenType::Star)
4005                            && self.is_connected()
4006                        {
4007                            self.skip(); // consume *
4008                            table_name.name.push('*');
4009                        }
4010                        let trailing_comments = self.previous_trailing_comments().to_vec();
4011                        // For 4-part names, combine first two parts as catalog, third as schema
4012                        Expression::boxed_table(TableRef {
4013                            catalog: Some(Identifier::new(format!(
4014                                "{}.{}",
4015                                first_name, second_name
4016                            ))),
4017                            schema: Some(third_ident),
4018                            name: table_name,
4019                            alias: None,
4020                            alias_explicit_as: false,
4021                            column_aliases: Vec::new(),
4022                            leading_comments: Vec::new(),
4023                            trailing_comments,
4024                            when: None,
4025                            only: false,
4026                            final_: false,
4027                            table_sample: None,
4028                            hints: Vec::new(),
4029                            system_time: None,
4030                            partitions: Vec::new(),
4031                            identifier_func: None,
4032                            changes: None,
4033                            version: None,
4034                            span: None,
4035                        })
4036                    } else if self.match_token(TokenType::LParen) {
4037                        // catalog.schema.function() - table-valued function
4038                        let args = if self.check(TokenType::RParen) {
4039                            Vec::new()
4040                        } else {
4041                            self.parse_function_arguments()?
4042                        };
4043                        self.expect(TokenType::RParen)?;
4044                        let trailing_comments = self.previous_trailing_comments().to_vec();
4045                        Expression::Function(Box::new(Function {
4046                            name: format!("{}.{}.{}", first_name, second_name, third_name),
4047                            args,
4048                            distinct: false,
4049                            trailing_comments,
4050                            use_bracket_syntax: false,
4051                            no_parens: false,
4052                            quoted: false,
4053                            span: None,
4054                            inferred_type: None,
4055                        }))
4056                    } else {
4057                        // catalog.schema.table
4058                        // BigQuery wildcard table suffix: x.y.z* matches all tables starting with z
4059                        let mut table_name = third_ident;
4060                        if matches!(
4061                            self.config.dialect,
4062                            Some(crate::dialects::DialectType::BigQuery)
4063                        ) && self.check(TokenType::Star)
4064                            && self.is_connected()
4065                        {
4066                            self.skip(); // consume *
4067                            table_name.name.push('*');
4068                        }
4069                        let trailing_comments = self.previous_trailing_comments().to_vec();
4070                        Expression::boxed_table(TableRef {
4071                            catalog: Some(first_ident),
4072                            schema: Some(second_ident),
4073                            name: table_name,
4074                            alias: None,
4075                            alias_explicit_as: false,
4076                            column_aliases: Vec::new(),
4077                            leading_comments: Vec::new(),
4078                            trailing_comments,
4079                            when: None,
4080                            only: false,
4081                            final_: false,
4082                            table_sample: None,
4083                            hints: Vec::new(),
4084                            system_time: None,
4085                            partitions: Vec::new(),
4086                            identifier_func: None,
4087                            changes: None,
4088                            version: None,
4089                            span: None,
4090                        })
4091                    }
4092                } else if self.match_token(TokenType::LParen) {
4093                    // schema.function() - table-valued function
4094                    let args = if self.check(TokenType::RParen) {
4095                        Vec::new()
4096                    } else {
4097                        self.parse_function_arguments()?
4098                    };
4099                    self.expect(TokenType::RParen)?;
4100                    let trailing_comments = self.previous_trailing_comments().to_vec();
4101                    Expression::Function(Box::new(Function {
4102                        name: format!("{}.{}", first_name, second_name),
4103                        args,
4104                        distinct: false,
4105                        trailing_comments,
4106                        use_bracket_syntax: false,
4107                        no_parens: false,
4108                        quoted: false,
4109                        span: None,
4110                        inferred_type: None,
4111                    }))
4112                } else {
4113                    // schema.table
4114                    // BigQuery wildcard table suffix: x.y* matches all tables starting with y
4115                    let mut table_name = second_ident;
4116                    if matches!(
4117                        self.config.dialect,
4118                        Some(crate::dialects::DialectType::BigQuery)
4119                    ) && self.check(TokenType::Star)
4120                        && self.is_connected()
4121                    {
4122                        self.skip(); // consume *
4123                        table_name.name.push('*');
4124                    }
4125                    let trailing_comments = self.previous_trailing_comments().to_vec();
4126                    Expression::boxed_table(TableRef {
4127                        catalog: None,
4128                        schema: Some(first_ident),
4129                        name: table_name,
4130                        alias: None,
4131                        alias_explicit_as: false,
4132                        column_aliases: Vec::new(),
4133                        leading_comments: Vec::new(),
4134                        trailing_comments,
4135                        when: None,
4136                        only: false,
4137                        final_: false,
4138                        table_sample: None,
4139                        hints: Vec::new(),
4140                        system_time: None,
4141                        partitions: Vec::new(),
4142                        identifier_func: None,
4143                        changes: None,
4144                        version: None,
4145                        span: None,
4146                    })
4147                }
4148            } else if self.match_token(TokenType::LParen) {
4149                // Handle JSON_TABLE specially - it has COLUMNS clause syntax
4150                if first_name.eq_ignore_ascii_case("JSON_TABLE") {
4151                    // Parse the JSON expression (use parse_bitwise to avoid consuming FORMAT)
4152                    let this = self
4153                        .parse_bitwise()?
4154                        .unwrap_or(Expression::Null(crate::expressions::Null));
4155
4156                    // Check for FORMAT JSON after the expression
4157                    let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
4158                        Expression::JSONFormat(Box::new(crate::expressions::JSONFormat {
4159                            this: Some(Box::new(this)),
4160                            options: Vec::new(),
4161                            is_json: None,
4162                            to_json: None,
4163                        }))
4164                    } else {
4165                        this
4166                    };
4167
4168                    // Parse path (after comma)
4169                    let path = if self.match_token(TokenType::Comma) {
4170                        if let Some(s) = self.parse_string()? {
4171                            Some(Box::new(s))
4172                        } else {
4173                            None
4174                        }
4175                    } else {
4176                        None
4177                    };
4178
4179                    // Oracle uses "ERROR ON ERROR" (value then behavior) instead of "ON ERROR ERROR"
4180                    // Parse error handling: ERROR ON ERROR or NULL ON ERROR
4181                    let error_handling = if self.match_identifier("ERROR")
4182                        && self.match_text_seq(&["ON", "ERROR"])
4183                    {
4184                        Some(Box::new(Expression::Var(Box::new(Var {
4185                            this: "ERROR ON ERROR".to_string(),
4186                        }))))
4187                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
4188                        Some(Box::new(Expression::Var(Box::new(Var {
4189                            this: "NULL ON ERROR".to_string(),
4190                        }))))
4191                    } else {
4192                        None
4193                    };
4194
4195                    // Parse empty handling: ERROR ON EMPTY or NULL ON EMPTY
4196                    let empty_handling = if self.match_identifier("ERROR")
4197                        && self.match_text_seq(&["ON", "EMPTY"])
4198                    {
4199                        Some(Box::new(Expression::Var(Box::new(Var {
4200                            this: "ERROR ON EMPTY".to_string(),
4201                        }))))
4202                    } else if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
4203                        Some(Box::new(Expression::Var(Box::new(Var {
4204                            this: "NULL ON EMPTY".to_string(),
4205                        }))))
4206                    } else {
4207                        None
4208                    };
4209
4210                    // Parse COLUMNS clause
4211                    let schema = self.parse_json_table_columns()?;
4212
4213                    self.expect(TokenType::RParen)?;
4214
4215                    Expression::JSONTable(Box::new(JSONTable {
4216                        this: Box::new(this_with_format),
4217                        schema: schema.map(Box::new),
4218                        path,
4219                        error_handling,
4220                        empty_handling,
4221                    }))
4222                } else if first_name.eq_ignore_ascii_case("XMLTABLE") {
4223                    // Handle XMLTABLE specially - it has COLUMNS clause syntax
4224                    // XMLTABLE([XMLNAMESPACES(...),] '/xpath' PASSING xml_doc COLUMNS ...)
4225                    if let Some(xml_table) = self.parse_xml_table()? {
4226                        self.expect(TokenType::RParen)?;
4227                        xml_table
4228                    } else {
4229                        return Err(self.parse_error("Failed to parse XMLTABLE"));
4230                    }
4231                } else if first_name.eq_ignore_ascii_case("OPENJSON") {
4232                    // Handle OPENJSON specially - it has WITH clause for column definitions
4233                    // OPENJSON(json[, path]) [WITH (col1 type1 'path' [AS JSON], ...)]
4234                    if let Some(openjson_expr) = self.parse_open_json()? {
4235                        openjson_expr
4236                    } else {
4237                        return Err(self.parse_error("Failed to parse OPENJSON"));
4238                    }
4239                } else if first_name.eq_ignore_ascii_case("SEMANTIC_VIEW") {
4240                    // Handle SEMANTIC_VIEW specially - it has METRICS/DIMENSIONS/FACTS/WHERE syntax
4241                    // SEMANTIC_VIEW(table METRICS a.b, a.c DIMENSIONS a.b, a.c WHERE expr)
4242                    let semantic_view = self.parse_semantic_view()?;
4243                    self.expect(TokenType::RParen)?;
4244                    semantic_view
4245                } else if (first_name.eq_ignore_ascii_case("view")
4246                    || first_name.eq_ignore_ascii_case("merge"))
4247                    && (self.check(TokenType::Select) || self.check(TokenType::With))
4248                {
4249                    // ClickHouse: view(SELECT ...) and merge(SELECT ...) table functions
4250                    // contain a subquery as the argument
4251                    let query = self.parse_statement()?;
4252                    self.expect(TokenType::RParen)?;
4253                    let trailing_comments = self.previous_trailing_comments().to_vec();
4254                    Expression::Function(Box::new(Function {
4255                        name: first_name.to_string(),
4256                        args: vec![query],
4257                        distinct: false,
4258                        trailing_comments,
4259                        use_bracket_syntax: false,
4260                        no_parens: false,
4261                        quoted: false,
4262                        span: None,
4263                        inferred_type: None,
4264                    }))
4265                } else {
4266                    // Simple table function like UNNEST(), GAP_FILL(), etc.
4267                    let args = if self.check(TokenType::RParen) {
4268                        Vec::new()
4269                    } else {
4270                        self.parse_function_arguments()?
4271                    };
4272                    self.expect(TokenType::RParen)?;
4273                    let trailing_comments = self.previous_trailing_comments().to_vec();
4274
4275                    // Handle UNNEST specially to create UnnestFunc expression
4276                    if first_name.eq_ignore_ascii_case("UNNEST") {
4277                        // Check for WITH ORDINALITY (Presto) or WITH OFFSET (BigQuery)
4278                        // Both are semantically the same - provide an ordinal/offset column
4279                        let with_ordinality = self
4280                            .match_keywords(&[TokenType::With, TokenType::Ordinality])
4281                            || self.match_text_seq(&["WITH", "OFFSET"]);
4282                        // If WITH OFFSET matched, check for optional offset alias: WITH OFFSET AS y or WITH OFFSET y
4283                        let offset_alias = if with_ordinality
4284                            && matches!(
4285                                self.config.dialect,
4286                                Some(crate::dialects::DialectType::BigQuery)
4287                            ) {
4288                            let has_as = self.match_token(TokenType::As);
4289                            if has_as
4290                                || (self.check(TokenType::Identifier) || self.check(TokenType::Var))
4291                            {
4292                                let alias_name = self.advance().text;
4293                                Some(crate::expressions::Identifier {
4294                                    name: alias_name,
4295                                    quoted: false,
4296                                    trailing_comments: Vec::new(),
4297                                    span: None,
4298                                })
4299                            } else {
4300                                None
4301                            }
4302                        } else {
4303                            None
4304                        };
4305                        let mut args_iter = args.into_iter();
4306                        let this = args_iter
4307                            .next()
4308                            .ok_or_else(|| self.parse_error("Expected expression in UNNEST"))?;
4309                        let expressions: Vec<Expression> = args_iter.collect();
4310                        Expression::Unnest(Box::new(crate::expressions::UnnestFunc {
4311                            this,
4312                            expressions,
4313                            with_ordinality,
4314                            alias: None,
4315                            offset_alias,
4316                        }))
4317                    } else {
4318                        // Check for WITH ORDINALITY after any table-valued function
4319                        let with_ordinality =
4320                            self.match_keywords(&[TokenType::With, TokenType::Ordinality]);
4321                        let func_name = if with_ordinality {
4322                            format!("{} WITH ORDINALITY", first_name)
4323                        } else {
4324                            first_name.clone()
4325                        };
4326                        let func = Function {
4327                            name: func_name,
4328                            args,
4329                            distinct: false,
4330                            trailing_comments,
4331                            use_bracket_syntax: false,
4332                            no_parens: false,
4333                            quoted: false,
4334                            span: None,
4335                            inferred_type: None,
4336                        };
4337                        let func_expr = Expression::Function(Box::new(func));
4338
4339                        // TSQL: OPENDATASOURCE(...).Catalog.schema.table
4340                        // After a table-valued function, dot-chained access produces
4341                        // a TableRef whose identifier_func holds the function call.
4342                        if self.check(TokenType::Dot) {
4343                            self.skip(); // consume first dot
4344                            let part1 = self.parse_bigquery_table_part()?;
4345                            if self.match_token(TokenType::Dot) {
4346                                let part2 = self.parse_bigquery_table_part()?;
4347                                if self.match_token(TokenType::Dot) {
4348                                    // func().a.b.c  → catalog=a, schema=b, name=c
4349                                    let part3 = self.parse_bigquery_table_part()?;
4350                                    let tc = self.previous_trailing_comments().to_vec();
4351                                    Expression::boxed_table(TableRef {
4352                                        catalog: Some(part1),
4353                                        schema: Some(part2),
4354                                        name: part3,
4355                                        alias: None,
4356                                        alias_explicit_as: false,
4357                                        column_aliases: Vec::new(),
4358                                        leading_comments: Vec::new(),
4359                                        trailing_comments: tc,
4360                                        when: None,
4361                                        only: false,
4362                                        final_: false,
4363                                        table_sample: None,
4364                                        hints: Vec::new(),
4365                                        system_time: None,
4366                                        partitions: Vec::new(),
4367                                        identifier_func: Some(Box::new(func_expr)),
4368                                        changes: None,
4369                                        version: None,
4370                                        span: None,
4371                                    })
4372                                } else {
4373                                    // func().a.b  → schema=a, name=b
4374                                    let tc = self.previous_trailing_comments().to_vec();
4375                                    Expression::boxed_table(TableRef {
4376                                        catalog: None,
4377                                        schema: Some(part1),
4378                                        name: part2,
4379                                        alias: None,
4380                                        alias_explicit_as: false,
4381                                        column_aliases: Vec::new(),
4382                                        leading_comments: Vec::new(),
4383                                        trailing_comments: tc,
4384                                        when: None,
4385                                        only: false,
4386                                        final_: false,
4387                                        table_sample: None,
4388                                        hints: Vec::new(),
4389                                        system_time: None,
4390                                        partitions: Vec::new(),
4391                                        identifier_func: Some(Box::new(func_expr)),
4392                                        changes: None,
4393                                        version: None,
4394                                        span: None,
4395                                    })
4396                                }
4397                            } else {
4398                                // func().a  → name=a
4399                                let tc = self.previous_trailing_comments().to_vec();
4400                                Expression::boxed_table(TableRef {
4401                                    catalog: None,
4402                                    schema: None,
4403                                    name: part1,
4404                                    alias: None,
4405                                    alias_explicit_as: false,
4406                                    column_aliases: Vec::new(),
4407                                    leading_comments: Vec::new(),
4408                                    trailing_comments: tc,
4409                                    when: None,
4410                                    only: false,
4411                                    final_: false,
4412                                    table_sample: None,
4413                                    hints: Vec::new(),
4414                                    system_time: None,
4415                                    partitions: Vec::new(),
4416                                    identifier_func: Some(Box::new(func_expr)),
4417                                    changes: None,
4418                                    version: None,
4419                                    span: None,
4420                                })
4421                            }
4422                        } else {
4423                            func_expr
4424                        }
4425                    }
4426                }
4427            } else {
4428                // Simple table name
4429                // BigQuery wildcard table suffix: x* matches all tables starting with x
4430                let mut table_name = first_ident;
4431                if matches!(
4432                    self.config.dialect,
4433                    Some(crate::dialects::DialectType::BigQuery)
4434                ) && self.check(TokenType::Star)
4435                    && self.is_connected()
4436                {
4437                    self.skip(); // consume *
4438                    table_name.name.push('*');
4439                }
4440                let trailing_comments = self.previous_trailing_comments().to_vec();
4441                Expression::boxed_table(TableRef {
4442                    catalog: None,
4443                    schema: None,
4444                    name: table_name,
4445                    alias: None,
4446                    alias_explicit_as: false,
4447                    column_aliases: Vec::new(),
4448                    leading_comments: Vec::new(),
4449                    trailing_comments,
4450                    when: None,
4451                    only: false,
4452                    final_: false,
4453                    table_sample: None,
4454                    hints: Vec::new(),
4455                    system_time: None,
4456                    partitions: Vec::new(),
4457                    identifier_func: None,
4458                    changes: None,
4459                    version: None,
4460                    span: None,
4461                })
4462            }
4463        } else if self.check(TokenType::LBrace) {
4464            // ClickHouse query parameter: {name: Type}
4465            if let Some(param) = self.parse_clickhouse_braced_parameter()? {
4466                param
4467            } else {
4468                // Spark/Databricks widget template variable: {name}
4469                self.skip(); // consume {
4470                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4471                    let name_token = self.advance();
4472                    self.expect(TokenType::RBrace)?;
4473                    Expression::Parameter(Box::new(Parameter {
4474                        name: Some(name_token.text.clone()),
4475                        index: None,
4476                        style: ParameterStyle::Brace,
4477                        quoted: false,
4478                        string_quoted: false,
4479                        expression: None,
4480                    }))
4481                } else {
4482                    return Err(self.parse_error("Expected identifier after {"));
4483                }
4484            }
4485        } else if self.check(TokenType::Dollar) && self.check_next(TokenType::LBrace) {
4486            // Template variable as table reference: ${variable_name} or ${kind:name}
4487            // This is used in Databricks/Hive for parameterized queries
4488            self.skip(); // consume $
4489            self.skip(); // consume {
4490            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4491                let name_token = self.advance();
4492                // Check for ${kind:name} syntax (e.g., ${hiveconf:some_var})
4493                let expression = if self.match_token(TokenType::Colon) {
4494                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4495                        let expr_token = self.advance();
4496                        Some(expr_token.text.clone())
4497                    } else {
4498                        return Err(self.parse_error("Expected identifier after : in ${...}"));
4499                    }
4500                } else {
4501                    None
4502                };
4503                self.expect(TokenType::RBrace)?;
4504                Expression::Parameter(Box::new(Parameter {
4505                    name: Some(name_token.text.clone()),
4506                    index: None,
4507                    style: ParameterStyle::DollarBrace,
4508                    quoted: false,
4509                    string_quoted: false,
4510                    expression,
4511                }))
4512            } else {
4513                return Err(self.parse_error("Expected identifier after ${"));
4514            }
4515        } else if self.check(TokenType::String) {
4516            // DuckDB allows string literals as table names: SELECT * FROM 'x.y'
4517            // Convert to a quoted identifier
4518            let string_token = self.advance();
4519            let table_name = Identifier {
4520                name: string_token.text.clone(),
4521                quoted: true,
4522                trailing_comments: Vec::new(),
4523                span: None,
4524            };
4525            let trailing_comments = self.previous_trailing_comments().to_vec();
4526            Expression::boxed_table(TableRef {
4527                catalog: None,
4528                schema: None,
4529                name: table_name,
4530                alias: None,
4531                alias_explicit_as: false,
4532                column_aliases: Vec::new(),
4533                leading_comments: Vec::new(),
4534                trailing_comments,
4535                when: None,
4536                only: false,
4537                final_: false,
4538                table_sample: None,
4539                hints: Vec::new(),
4540                system_time: None,
4541                partitions: Vec::new(),
4542                identifier_func: None,
4543                changes: None,
4544                version: None,
4545                span: None,
4546            })
4547        } else {
4548            return Err(self.parse_error(format!(
4549                "Expected table name or subquery, got {:?}",
4550                self.peek().token_type
4551            )));
4552        };
4553
4554        // Postgres supports a wildcard (table) suffix operator, which is a no-op in this context.
4555        // e.g., FROM t1* means "include inherited tables". Matches Python sqlglot behavior.
4556        self.match_token(TokenType::Star);
4557
4558        // Check for Snowflake CHANGES clause: CHANGES (INFORMATION => ...) AT|BEFORE (...) END (...)
4559        // Must be checked before time travel since CHANGES includes its own AT/BEFORE clauses
4560        if self.check_keyword_text("CHANGES") {
4561            if let Some(changes_expr) = self.parse_changes()? {
4562                if let Expression::Table(ref mut table) = expr {
4563                    if let Expression::Changes(changes_box) = changes_expr {
4564                        table.changes = Some(changes_box);
4565                    }
4566                }
4567            }
4568        }
4569
4570        // Check for Snowflake time travel: BEFORE (STATEMENT => ...) or AT (TIMESTAMP => ...)
4571        if self.check(TokenType::Before) || self.check_keyword_text("AT") {
4572            if let Some(historical_expr) = self.parse_historical_data()? {
4573                // Attach historical data to the table expression
4574                if let Expression::Table(ref mut table) = expr {
4575                    if let Expression::HistoricalData(hd) = historical_expr {
4576                        table.when = Some(hd);
4577                    }
4578                }
4579            }
4580        }
4581
4582        // Check for TSQL FOR SYSTEM_TIME temporal clause (not BigQuery - handled post-alias)
4583        // Syntax: FOR SYSTEM_TIME AS OF expr
4584        //         FOR SYSTEM_TIME FROM expr TO expr
4585        //         FOR SYSTEM_TIME BETWEEN expr AND expr
4586        //         FOR SYSTEM_TIME CONTAINED IN (expr, expr)
4587        //         FOR SYSTEM_TIME ALL
4588        if !matches!(
4589            self.config.dialect,
4590            Some(crate::dialects::DialectType::BigQuery)
4591        ) && self.check(TokenType::For)
4592            && self.current + 1 < self.tokens.len()
4593            && self.tokens[self.current + 1]
4594                .text
4595                .eq_ignore_ascii_case("SYSTEM_TIME")
4596        {
4597            self.skip(); // consume FOR
4598            self.skip(); // consume SYSTEM_TIME
4599            let system_time_str = if self.match_token(TokenType::As) {
4600                // AS OF expr
4601                if self.check_keyword_text("OF") {
4602                    self.skip(); // consume OF
4603                    let start = self.current;
4604                    // Collect expression tokens until we hit a clause boundary
4605                    while !self.is_at_end()
4606                        && !self.check(TokenType::Semicolon)
4607                        && !self.check(TokenType::Where)
4608                        && !self.check(TokenType::Join)
4609                        && !self.check(TokenType::Left)
4610                        && !self.check(TokenType::Right)
4611                        && !self.check(TokenType::Inner)
4612                        && !self.check(TokenType::Outer)
4613                        && !self.check(TokenType::Full)
4614                        && !self.check(TokenType::Cross)
4615                        && !self.check(TokenType::Order)
4616                        && !self.check(TokenType::Group)
4617                        && !self.check(TokenType::Having)
4618                        && !self.check(TokenType::Limit)
4619                        && !self.check(TokenType::Union)
4620                        && !self.check(TokenType::Except)
4621                        && !self.check(TokenType::Intersect)
4622                        && !self.check(TokenType::As)
4623                        && !self.check(TokenType::Comma)
4624                        && !self.check(TokenType::RParen)
4625                        && !self.check(TokenType::With)
4626                        && !self.check(TokenType::Pivot)
4627                        && !self.check(TokenType::Unpivot)
4628                    {
4629                        self.skip();
4630                    }
4631                    let expr_text = self.tokens_to_sql_uppercased(start, self.current);
4632                    format!("FOR SYSTEM_TIME AS OF {}", expr_text)
4633                } else {
4634                    "FOR SYSTEM_TIME AS".to_string()
4635                }
4636            } else if self.match_token(TokenType::Between) {
4637                // BETWEEN expr AND expr
4638                let start = self.current;
4639                while !self.is_at_end() && !self.check(TokenType::And) {
4640                    self.skip();
4641                }
4642                let expr1_text = self.tokens_to_sql_uppercased(start, self.current);
4643                self.skip(); // consume AND
4644                let start2 = self.current;
4645                while !self.is_at_end()
4646                    && !self.check(TokenType::Semicolon)
4647                    && !self.check(TokenType::Where)
4648                    && !self.check(TokenType::Join)
4649                    && !self.check(TokenType::Left)
4650                    && !self.check(TokenType::Right)
4651                    && !self.check(TokenType::Inner)
4652                    && !self.check(TokenType::Outer)
4653                    && !self.check(TokenType::Full)
4654                    && !self.check(TokenType::Cross)
4655                    && !self.check(TokenType::Order)
4656                    && !self.check(TokenType::Group)
4657                    && !self.check(TokenType::Having)
4658                    && !self.check(TokenType::Limit)
4659                    && !self.check(TokenType::Union)
4660                    && !self.check(TokenType::Except)
4661                    && !self.check(TokenType::Intersect)
4662                    && !self.check(TokenType::As)
4663                    && !self.check(TokenType::Comma)
4664                    && !self.check(TokenType::RParen)
4665                    && !self.check(TokenType::With)
4666                    && !self.check(TokenType::Pivot)
4667                    && !self.check(TokenType::Unpivot)
4668                {
4669                    self.skip();
4670                }
4671                let expr2_text = self.tokens_to_sql_uppercased(start2, self.current);
4672                format!("FOR SYSTEM_TIME BETWEEN {} AND {}", expr1_text, expr2_text)
4673            } else if self.match_token(TokenType::From) {
4674                // FROM expr TO expr
4675                let start = self.current;
4676                while !self.is_at_end() && !self.check(TokenType::To) {
4677                    self.skip();
4678                }
4679                let expr1_text = self.tokens_to_sql_uppercased(start, self.current);
4680                self.skip(); // consume TO
4681                let start2 = self.current;
4682                while !self.is_at_end()
4683                    && !self.check(TokenType::Semicolon)
4684                    && !self.check(TokenType::Where)
4685                    && !self.check(TokenType::As)
4686                    && !self.check(TokenType::Comma)
4687                    && !self.check(TokenType::RParen)
4688                {
4689                    self.skip();
4690                }
4691                let expr2_text = self.tokens_to_sql_uppercased(start2, self.current);
4692                format!("FOR SYSTEM_TIME FROM {} TO {}", expr1_text, expr2_text)
4693            } else if self.check_identifier("CONTAINED") {
4694                self.skip(); // consume CONTAINED
4695                self.expect(TokenType::In)?;
4696                self.expect(TokenType::LParen)?;
4697                let start = self.current;
4698                let mut depth = 1;
4699                while !self.is_at_end() && depth > 0 {
4700                    if self.check(TokenType::LParen) {
4701                        depth += 1;
4702                    }
4703                    if self.check(TokenType::RParen) {
4704                        depth -= 1;
4705                        if depth == 0 {
4706                            break;
4707                        }
4708                    }
4709                    self.skip();
4710                }
4711                let inner_text = self.tokens_to_sql_uppercased(start, self.current);
4712                self.expect(TokenType::RParen)?;
4713                format!("FOR SYSTEM_TIME CONTAINED IN ({})", inner_text)
4714            } else if self.match_token(TokenType::All) {
4715                "FOR SYSTEM_TIME ALL".to_string()
4716            } else {
4717                "FOR SYSTEM_TIME".to_string()
4718            };
4719            if let Expression::Table(ref mut table) = expr {
4720                table.system_time = Some(system_time_str);
4721            }
4722        }
4723
4724        // Check for Presto/Trino time travel: FOR VERSION AS OF / FOR TIMESTAMP AS OF
4725        // Syntax: FOR VERSION AS OF <snapshot_id>
4726        //         FOR TIMESTAMP AS OF <timestamp_expr>
4727        if self.check(TokenType::For) && self.current + 1 < self.tokens.len() {
4728            let next_text = &self.tokens[self.current + 1].text;
4729            if next_text.eq_ignore_ascii_case("VERSION")
4730                || next_text.eq_ignore_ascii_case("TIMESTAMP")
4731            {
4732                self.skip(); // consume FOR
4733                let version_kind = self.advance().text.to_ascii_uppercase(); // consume VERSION or TIMESTAMP
4734
4735                // Expect AS OF
4736                if self.match_token(TokenType::As) && self.check_keyword_text("OF") {
4737                    self.skip(); // consume OF
4738
4739                    // Parse the expression value
4740                    if let Some(value_expr) = self.parse_bitwise()? {
4741                        let version = crate::expressions::Version {
4742                            this: Box::new(Expression::Identifier(Identifier::new(&version_kind))),
4743                            kind: "AS OF".to_string(),
4744                            expression: Some(Box::new(value_expr)),
4745                        };
4746                        if let Expression::Table(ref mut table) = expr {
4747                            table.version = Some(Box::new(version));
4748                        }
4749                    }
4750                }
4751            }
4752        }
4753
4754        // Check for Hive-style time travel: TIMESTAMP AS OF / VERSION AS OF (without FOR)
4755        // Syntax: TIMESTAMP AS OF <timestamp_expr>
4756        //         VERSION AS OF <snapshot_id>
4757        if self.current < self.tokens.len() {
4758            let current_text = &self.tokens[self.current].text;
4759            if (current_text.eq_ignore_ascii_case("TIMESTAMP")
4760                || current_text.eq_ignore_ascii_case("VERSION"))
4761                && self.current + 2 < self.tokens.len()
4762                && self.tokens[self.current + 1].token_type == TokenType::As
4763                && self.tokens[self.current + 2]
4764                    .text
4765                    .eq_ignore_ascii_case("OF")
4766            {
4767                let version_kind = self.advance().text.to_ascii_uppercase(); // consume TIMESTAMP or VERSION
4768                self.skip(); // consume AS
4769                self.skip(); // consume OF
4770
4771                // Parse the expression value
4772                if let Some(value_expr) = self.parse_bitwise()? {
4773                    let version = crate::expressions::Version {
4774                        this: Box::new(Expression::Identifier(Identifier::new(&version_kind))),
4775                        kind: "AS OF".to_string(),
4776                        expression: Some(Box::new(value_expr)),
4777                    };
4778                    if let Expression::Table(ref mut table) = expr {
4779                        table.version = Some(Box::new(version));
4780                    }
4781                }
4782            }
4783        }
4784
4785        // Check for MySQL PARTITION(p0, p1, ...) clause
4786        // Only supported by MySQL-compatible dialects (not generic dialect)
4787        let supports_partition_selection = matches!(
4788            self.config.dialect,
4789            Some(crate::dialects::DialectType::MySQL)
4790                | Some(crate::dialects::DialectType::SingleStore)
4791                | Some(crate::dialects::DialectType::Doris)
4792                | Some(crate::dialects::DialectType::StarRocks)
4793        );
4794        if supports_partition_selection && self.match_token(TokenType::Partition) {
4795            if self.match_token(TokenType::LParen) {
4796                let mut partitions = Vec::new();
4797                loop {
4798                    let partition_name = self.expect_identifier_or_keyword_with_quoted()?;
4799                    partitions.push(partition_name);
4800                    if !self.match_token(TokenType::Comma) {
4801                        break;
4802                    }
4803                }
4804                self.expect(TokenType::RParen)?;
4805                if let Expression::Table(ref mut table) = expr {
4806                    table.partitions = partitions;
4807                }
4808            }
4809        }
4810
4811        // Check for table-level TABLESAMPLE/SAMPLE: tbl TABLESAMPLE METHOD(size) or tbl SAMPLE ROW(0)
4812        // Snowflake supports both TABLESAMPLE and SAMPLE
4813        if self.check(TokenType::TableSample) || self.check(TokenType::Sample) {
4814            if let Some(sample) = self.parse_table_level_sample()? {
4815                if let Expression::Table(ref mut table) = expr {
4816                    table.table_sample = Some(Box::new(sample));
4817                } else {
4818                    // For non-Table expressions (subqueries, functions, etc.),
4819                    // wrap in TableSample expression node
4820                    expr = Expression::TableSample(Box::new(crate::expressions::TableSample {
4821                        this: Some(Box::new(expr)),
4822                        sample: Some(Box::new(sample)),
4823                        expressions: Vec::new(),
4824                        method: None,
4825                        bucket_numerator: None,
4826                        bucket_denominator: None,
4827                        bucket_field: None,
4828                        percent: None,
4829                        rows: None,
4830                        size: None,
4831                        seed: None,
4832                    }));
4833                }
4834            }
4835        }
4836
4837        // Check for TSQL table hints: WITH (TABLOCK, INDEX(myindex), ...)
4838        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
4839            if let Expression::Table(ref mut table) = expr {
4840                if let Some(hint_expr) = self.parse_table_hints()? {
4841                    // parse_table_hints returns a Tuple wrapping individual hint expressions.
4842                    // Extract the inner hints so we store them directly.
4843                    match hint_expr {
4844                        Expression::Tuple(tuple) => {
4845                            table.hints = tuple.expressions;
4846                        }
4847                        other => {
4848                            table.hints = vec![other];
4849                        }
4850                    }
4851                }
4852            }
4853        }
4854
4855        // Check for MySQL index hints: USE INDEX, IGNORE INDEX, FORCE INDEX
4856        if self.check_keyword_text("USE")
4857            || self.check(TokenType::Ignore)
4858            || self.check_keyword_text("FORCE")
4859        {
4860            // Peek ahead to see if next token after USE/IGNORE/FORCE is INDEX or KEY
4861            let next_idx = self.current + 1;
4862            let is_index_hint = next_idx < self.tokens.len() && {
4863                let next_text = &self.tokens[next_idx].text;
4864                next_text.eq_ignore_ascii_case("INDEX") || next_text.eq_ignore_ascii_case("KEY")
4865            };
4866            if is_index_hint {
4867                if let Expression::Table(ref mut table) = expr {
4868                    if let Some(hint_expr) = self.parse_table_hints()? {
4869                        match hint_expr {
4870                            Expression::Tuple(tuple) => {
4871                                table.hints = tuple.expressions;
4872                            }
4873                            other => {
4874                                table.hints = vec![other];
4875                            }
4876                        }
4877                    }
4878                }
4879            }
4880        }
4881
4882        // Check for SQLite INDEXED BY or NOT INDEXED table hints
4883        if self.check_identifier("INDEXED") {
4884            self.skip(); // consume INDEXED
4885            self.expect(TokenType::By)?;
4886            // Parse index name (can be qualified: schema.index)
4887            let first_part = self.expect_identifier_or_keyword()?;
4888            let index_name = if self.match_token(TokenType::Dot) {
4889                let second_part = self.expect_identifier_or_keyword()?;
4890                format!("{}.{}", first_part, second_part)
4891            } else {
4892                first_part
4893            };
4894            if let Expression::Table(ref mut table) = expr {
4895                table.hints.push(Expression::Identifier(Identifier {
4896                    name: format!("INDEXED BY {}", index_name),
4897                    quoted: false,
4898                    trailing_comments: Vec::new(),
4899                    span: None,
4900                }));
4901            }
4902        } else if self.check(TokenType::Not) && self.check_next_identifier("INDEXED") {
4903            self.skip(); // consume NOT
4904            self.skip(); // consume INDEXED
4905            if let Expression::Table(ref mut table) = expr {
4906                table.hints.push(Expression::Identifier(Identifier {
4907                    name: "NOT INDEXED".to_string(),
4908                    quoted: false,
4909                    trailing_comments: Vec::new(),
4910                    span: None,
4911                }));
4912            }
4913        }
4914
4915        // Check for PIVOT (can be followed by UNPIVOT)
4916        // Only treat as PIVOT clause when followed by ( — otherwise it's a table alias
4917        if self.check(TokenType::Pivot) && self.check_next(TokenType::LParen) {
4918            self.skip(); // consume PIVOT
4919            expr = self.parse_pivot(expr)?;
4920        }
4921        // Check for UNPIVOT (can follow PIVOT or be standalone)
4922        // Only treat as UNPIVOT clause when followed by (, INCLUDE, or EXCLUDE — otherwise it's a table alias
4923        if self.check(TokenType::Unpivot) && self.is_unpivot_clause_start() {
4924            self.skip(); // consume UNPIVOT
4925            expr = self.parse_unpivot(expr)?;
4926        }
4927        // Check for MATCH_RECOGNIZE
4928        else if self.check(TokenType::MatchRecognize)
4929            && !matches!(&expr, Expression::Pivot(_) | Expression::Unpivot(_))
4930        {
4931            self.skip();
4932            expr = self.parse_match_recognize(Some(expr))?;
4933        }
4934
4935        // Check for alias
4936        if self.match_token(TokenType::As) {
4937            // Handle AS (col1, col2) without alias name - used by POSEXPLODE etc.
4938            if self.check(TokenType::LParen) {
4939                self.skip(); // consume LParen
4940                let mut column_aliases = Vec::new();
4941                loop {
4942                    if self.check(TokenType::RParen) {
4943                        break;
4944                    }
4945                    column_aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
4946                    if !self.match_token(TokenType::Comma) {
4947                        break;
4948                    }
4949                }
4950                self.expect(TokenType::RParen)?;
4951                expr = Expression::Alias(Box::new(Alias {
4952                    this: expr,
4953                    alias: Identifier::new(String::new()),
4954                    column_aliases,
4955                    pre_alias_comments: Vec::new(),
4956                    trailing_comments: Vec::new(),
4957                    inferred_type: None,
4958                }));
4959            } else {
4960                let alias_ident_parsed = self.expect_identifier_or_alias_keyword_with_quoted()?;
4961                let alias = alias_ident_parsed.name;
4962                let alias_is_quoted = alias_ident_parsed.quoted;
4963                let make_alias_ident = |name: String| -> Identifier {
4964                    if alias_is_quoted {
4965                        Identifier::quoted(name)
4966                    } else {
4967                        Identifier::new(name)
4968                    }
4969                };
4970                // Check for column aliases: AS t(c1, c2) or AS t(c1 type1, c2 type2) for table functions
4971                if self.match_token(TokenType::LParen) {
4972                    // Check if this is typed column definitions (for table functions like JSON_TO_RECORDSET)
4973                    // by looking ahead: if we see identifier followed by another identifier/type (not comma/rparen),
4974                    // it's typed columns
4975                    let has_typed_columns = self.check_typed_column_list();
4976
4977                    if has_typed_columns {
4978                        // Parse typed column definitions like: (col1 type1, col2 type2)
4979                        let mut typed_cols = Vec::new();
4980                        loop {
4981                            if self.check(TokenType::RParen) {
4982                                break;
4983                            }
4984                            // Parse column name (can be quoted)
4985                            let col_name = self.expect_identifier_or_keyword_with_quoted()?;
4986                            // Parse column type
4987                            let col_type = self.parse_data_type()?;
4988                            // Create ColumnDef expression, preserving the quoted status
4989                            let mut col_def = ColumnDef::new(col_name.name.clone(), col_type);
4990                            col_def.name = col_name;
4991                            typed_cols.push(Expression::ColumnDef(Box::new(col_def)));
4992
4993                            if !self.match_token(TokenType::Comma) {
4994                                break;
4995                            }
4996                        }
4997                        self.expect(TokenType::RParen)?;
4998
4999                        // Create TableAlias with typed columns
5000                        let table_alias = Expression::TableAlias(Box::new(TableAlias {
5001                            this: Some(Box::new(Expression::Identifier(make_alias_ident(alias)))),
5002                            columns: typed_cols,
5003                        }));
5004
5005                        // Wrap function with TableAlias using Tuple pattern (like ROWS FROM)
5006                        expr = Expression::Tuple(Box::new(Tuple {
5007                            expressions: vec![expr, table_alias],
5008                        }));
5009                    } else {
5010                        // Parse simple column aliases: (c1, c2, ...)
5011                        // Use expect_identifier_or_keyword to allow keywords like KEY, INDEX, VALUE as column aliases
5012                        let mut aliases = Vec::new();
5013                        loop {
5014                            if self.check(TokenType::RParen) {
5015                                break;
5016                            }
5017                            aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
5018                            if !self.match_token(TokenType::Comma) {
5019                                break;
5020                            }
5021                        }
5022                        self.expect(TokenType::RParen)?;
5023
5024                        expr = match expr {
5025                            Expression::Table(mut t) => {
5026                                t.alias = Some(make_alias_ident(alias));
5027                                t.alias_explicit_as = true;
5028                                t.column_aliases = aliases;
5029                                Expression::Table(t)
5030                            }
5031                            Expression::Subquery(mut s) => {
5032                                s.alias = Some(make_alias_ident(alias));
5033                                s.column_aliases = aliases;
5034                                Expression::Subquery(s)
5035                            }
5036                            Expression::Pivot(mut p) => {
5037                                p.alias = Some(make_alias_ident(alias));
5038                                Expression::Pivot(p)
5039                            }
5040                            Expression::Unpivot(mut u) => {
5041                                u.alias = Some(make_alias_ident(alias));
5042                                Expression::Unpivot(u)
5043                            }
5044                            Expression::MatchRecognize(mut mr) => {
5045                                mr.alias = Some(make_alias_ident(alias));
5046                                mr.alias_explicit_as = true;
5047                                Expression::MatchRecognize(mr)
5048                            }
5049                            Expression::JoinedTable(mut jt) => {
5050                                jt.alias = Some(make_alias_ident(alias));
5051                                Expression::JoinedTable(jt)
5052                            }
5053                            _ => Expression::Alias(Box::new(Alias {
5054                                this: expr,
5055                                alias: make_alias_ident(alias),
5056                                column_aliases: aliases,
5057                                pre_alias_comments: Vec::new(),
5058                                trailing_comments: Vec::new(),
5059                                inferred_type: None,
5060                            })),
5061                        };
5062                    }
5063                } else {
5064                    // No column aliases, just simple alias
5065                    let default_column_aliases = if matches!(
5066                        self.config.dialect,
5067                        Some(crate::dialects::DialectType::ClickHouse)
5068                    ) && matches!(&expr, Expression::Function(func) if func.name.eq_ignore_ascii_case("generate_series"))
5069                    {
5070                        vec![Identifier::new("generate_series")]
5071                    } else {
5072                        Vec::new()
5073                    };
5074                    expr = match expr {
5075                        Expression::Table(mut t) => {
5076                            t.alias = Some(make_alias_ident(alias));
5077                            t.alias_explicit_as = true;
5078                            t.column_aliases = Vec::new();
5079                            Expression::Table(t)
5080                        }
5081                        Expression::Subquery(mut s) => {
5082                            s.alias = Some(make_alias_ident(alias));
5083                            s.column_aliases = Vec::new();
5084                            Expression::Subquery(s)
5085                        }
5086                        Expression::Pivot(mut p) => {
5087                            p.alias = Some(make_alias_ident(alias));
5088                            Expression::Pivot(p)
5089                        }
5090                        Expression::Unpivot(mut u) => {
5091                            u.alias = Some(make_alias_ident(alias));
5092                            Expression::Unpivot(u)
5093                        }
5094                        Expression::MatchRecognize(mut mr) => {
5095                            mr.alias = Some(make_alias_ident(alias));
5096                            mr.alias_explicit_as = true;
5097                            Expression::MatchRecognize(mr)
5098                        }
5099                        Expression::JoinedTable(mut jt) => {
5100                            jt.alias = Some(make_alias_ident(alias));
5101                            Expression::JoinedTable(jt)
5102                        }
5103                        _ => Expression::Alias(Box::new(Alias {
5104                            this: expr,
5105                            alias: make_alias_ident(alias),
5106                            column_aliases: default_column_aliases,
5107                            pre_alias_comments: Vec::new(),
5108                            trailing_comments: Vec::new(),
5109                            inferred_type: None,
5110                        })),
5111                    };
5112                }
5113            } // close the else for AS (col1, col2) handling
5114        } else if (self.check(TokenType::QuotedIdentifier)
5115            || (self.check(TokenType::Var) && !self.check_keyword() && !self.check_identifier("MATCH_CONDITION")
5116                && !(self.check_identifier("ARRAY") && self.check_next(TokenType::Join)
5117                     && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
5118                // TSQL: OPTION(LABEL = 'foo') is a query hint, not an alias
5119                && !(self.check_identifier("OPTION") && self.check_next(TokenType::LParen))
5120                // MySQL: LOCK IN SHARE MODE is a locking clause, not an alias
5121                && !(self.check_identifier("LOCK") && self.check_next(TokenType::In))
5122                // ClickHouse: PARALLEL WITH is a statement separator, not a table alias
5123                && !(self.check_identifier("PARALLEL") && self.check_next(TokenType::With)
5124                     && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
5125                // DuckDB: POSITIONAL JOIN is a join method, not a table alias
5126                && !(self.check_identifier("POSITIONAL") && self.check_next(TokenType::Join))))
5127            || self.is_command_keyword_as_alias()
5128            // ClickHouse: allow FIRST/LAST as implicit table aliases
5129            // (they're keywords used in NULLS FIRST/LAST but also valid as identifiers)
5130            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
5131                && (self.check(TokenType::First) || self.check(TokenType::Last)))
5132            // PIVOT/UNPIVOT can be table aliases when not followed by clause-starting tokens
5133            || (self.check(TokenType::Pivot) && !self.check_next(TokenType::LParen))
5134            || (self.check(TokenType::Unpivot) && !self.is_unpivot_clause_start())
5135            // PARTITION can be a table alias when the dialect doesn't support partition selection
5136            || (self.check(TokenType::Partition) && !matches!(
5137                self.config.dialect,
5138                Some(crate::dialects::DialectType::MySQL)
5139                | Some(crate::dialects::DialectType::SingleStore)
5140                | Some(crate::dialects::DialectType::Doris)
5141                | Some(crate::dialects::DialectType::StarRocks)
5142            ))
5143            || (self.check(TokenType::Window) && {
5144                // WINDOW can be a table alias if NOT followed by an identifier (window definition)
5145                let next_pos = self.current + 1;
5146                next_pos >= self.tokens.len()
5147                    || (self.tokens[next_pos].token_type != TokenType::Var
5148                        && self.tokens[next_pos].token_type != TokenType::Identifier)
5149            })
5150        {
5151            // Implicit alias (but not MATCH_CONDITION which is a join condition keyword)
5152            // Also allow command keywords (GET, PUT, etc.) and WINDOW (when not a clause) as implicit table aliases
5153            let is_keyword_alias = self.peek().token_type.is_keyword();
5154            let is_quoted_alias = self.peek().token_type == TokenType::QuotedIdentifier;
5155            let alias = self.advance().text.clone();
5156            // Check for column aliases: t(c1, c2)
5157            // Use expect_identifier_or_keyword to allow keywords like KEY, INDEX, VALUE as column aliases
5158            let mut column_aliases = if self.match_token(TokenType::LParen) {
5159                let mut aliases = Vec::new();
5160                loop {
5161                    aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
5162                    if !self.match_token(TokenType::Comma) {
5163                        break;
5164                    }
5165                }
5166                self.expect(TokenType::RParen)?;
5167                aliases
5168            } else {
5169                Vec::new()
5170            };
5171            if column_aliases.is_empty()
5172                && matches!(
5173                    self.config.dialect,
5174                    Some(crate::dialects::DialectType::ClickHouse)
5175                )
5176                && matches!(&expr, Expression::Function(func) if func.name.eq_ignore_ascii_case("generate_series"))
5177            {
5178                column_aliases = vec![Identifier::new("generate_series")];
5179            }
5180            let make_alias_ident = |name: String| -> Identifier {
5181                if is_quoted_alias {
5182                    Identifier::quoted(name)
5183                } else {
5184                    Identifier::new(name)
5185                }
5186            };
5187            expr = match expr {
5188                Expression::Table(mut t) => {
5189                    t.alias = Some(make_alias_ident(alias));
5190                    t.alias_explicit_as = is_keyword_alias;
5191                    t.column_aliases = column_aliases;
5192                    Expression::Table(t)
5193                }
5194                Expression::Subquery(mut s) => {
5195                    s.alias = Some(make_alias_ident(alias));
5196                    s.column_aliases = column_aliases;
5197                    Expression::Subquery(s)
5198                }
5199                Expression::Pivot(mut p) => {
5200                    p.alias = Some(make_alias_ident(alias));
5201                    Expression::Pivot(p)
5202                }
5203                Expression::Unpivot(mut u) => {
5204                    u.alias = Some(make_alias_ident(alias));
5205                    Expression::Unpivot(u)
5206                }
5207                Expression::MatchRecognize(mut mr) => {
5208                    mr.alias = Some(make_alias_ident(alias));
5209                    Expression::MatchRecognize(mr)
5210                }
5211                Expression::JoinedTable(mut jt) => {
5212                    jt.alias = Some(make_alias_ident(alias));
5213                    Expression::JoinedTable(jt)
5214                }
5215                _ => Expression::Alias(Box::new(Alias {
5216                    this: expr,
5217                    alias: make_alias_ident(alias),
5218                    column_aliases,
5219                    pre_alias_comments: Vec::new(),
5220                    trailing_comments: Vec::new(),
5221                    inferred_type: None,
5222                })),
5223            };
5224        }
5225
5226        // ClickHouse: subquery column alias list without alias name: FROM (...) (c0, c1)
5227        if matches!(
5228            self.config.dialect,
5229            Some(crate::dialects::DialectType::ClickHouse)
5230        ) && self.check(TokenType::LParen)
5231            && matches!(&expr, Expression::Subquery(s) if s.alias.is_none())
5232        {
5233            // Lookahead: check if this is (identifier, identifier, ...) — column alias list
5234            let mut look = self.current + 1;
5235            let mut is_col_list = true;
5236            let mut col_count = 0;
5237            loop {
5238                if look >= self.tokens.len() {
5239                    is_col_list = false;
5240                    break;
5241                }
5242                let tt = self.tokens[look].token_type;
5243                if tt == TokenType::Identifier
5244                    || tt == TokenType::Var
5245                    || tt == TokenType::QuotedIdentifier
5246                    || tt.is_keyword()
5247                {
5248                    col_count += 1;
5249                    look += 1;
5250                } else {
5251                    is_col_list = false;
5252                    break;
5253                }
5254                if look >= self.tokens.len() {
5255                    is_col_list = false;
5256                    break;
5257                }
5258                if self.tokens[look].token_type == TokenType::Comma {
5259                    look += 1;
5260                } else if self.tokens[look].token_type == TokenType::RParen {
5261                    break;
5262                } else {
5263                    is_col_list = false;
5264                    break;
5265                }
5266            }
5267            if is_col_list && col_count >= 1 {
5268                self.skip(); // consume LParen
5269                let mut aliases = Vec::new();
5270                loop {
5271                    aliases.push(Identifier::new(self.advance().text.clone()));
5272                    if !self.match_token(TokenType::Comma) {
5273                        break;
5274                    }
5275                }
5276                self.expect(TokenType::RParen)?;
5277                if let Expression::Subquery(ref mut s) = expr {
5278                    s.column_aliases = aliases;
5279                }
5280            }
5281        }
5282
5283        // ClickHouse FINAL modifier: table [AS alias] FINAL
5284        if matches!(
5285            self.config.dialect,
5286            Some(crate::dialects::DialectType::ClickHouse)
5287        ) && self.match_token(TokenType::Final)
5288        {
5289            if let Expression::Table(ref mut table) = expr {
5290                table.final_ = true;
5291            }
5292        }
5293
5294        // Check for SQLite INDEXED BY after alias: t AS t INDEXED BY idx
5295        if self.check_identifier("INDEXED") {
5296            self.skip(); // consume INDEXED
5297            self.expect(TokenType::By)?;
5298            let first_part = self.expect_identifier_or_keyword()?;
5299            let index_name = if self.match_token(TokenType::Dot) {
5300                let second_part = self.expect_identifier_or_keyword()?;
5301                format!("{}.{}", first_part, second_part)
5302            } else {
5303                first_part
5304            };
5305            if let Expression::Table(ref mut table) = expr {
5306                table.hints.push(Expression::Identifier(Identifier {
5307                    name: format!("INDEXED BY {}", index_name),
5308                    quoted: false,
5309                    trailing_comments: Vec::new(),
5310                    span: None,
5311                }));
5312            }
5313        }
5314
5315        // Check for MySQL index hints after alias: t e USE INDEX (idx), t AS a IGNORE INDEX (idx)
5316        if self.check_keyword_text("USE")
5317            || self.check(TokenType::Ignore)
5318            || self.check_keyword_text("FORCE")
5319        {
5320            let next_idx = self.current + 1;
5321            let is_index_hint = next_idx < self.tokens.len() && {
5322                let next_text = &self.tokens[next_idx].text;
5323                next_text.eq_ignore_ascii_case("INDEX") || next_text.eq_ignore_ascii_case("KEY")
5324            };
5325            if is_index_hint {
5326                if let Expression::Table(ref mut table) = expr {
5327                    if let Some(hint_expr) = self.parse_table_hints()? {
5328                        match hint_expr {
5329                            Expression::Tuple(tuple) => {
5330                                table.hints = tuple.expressions;
5331                            }
5332                            other => {
5333                                table.hints = vec![other];
5334                            }
5335                        }
5336                    }
5337                }
5338            }
5339        }
5340
5341        // Check for PIVOT/UNPIVOT after alias (some dialects allow this order)
5342        // Only treat as PIVOT/UNPIVOT clause when followed by ( — otherwise it's a table alias
5343        if self.check(TokenType::Pivot) && self.check_next(TokenType::LParen) {
5344            self.skip(); // consume PIVOT
5345            expr = self.parse_pivot(expr)?;
5346        } else if self.check(TokenType::Unpivot) && self.is_unpivot_clause_start() {
5347            self.skip(); // consume UNPIVOT
5348            expr = self.parse_unpivot(expr)?;
5349        }
5350
5351        // Check for Redshift AT index clause for array unnesting
5352        // Syntax: table_alias.array_column AS element_alias AT index_alias
5353        // e.g., c.c_orders AS orders AT index
5354        // https://docs.aws.amazon.com/redshift/latest/dg/query-super.html
5355        if self.match_identifier("AT") {
5356            let index_alias = self.expect_identifier_or_keyword()?;
5357            // Convert the table expression to a column for AtIndex
5358            let column_expr = match expr {
5359                Expression::Table(t) => {
5360                    // Convert Table to Column reference
5361                    // For c.c_orders, table=c, name=c_orders -> column name should be c.c_orders
5362                    let mut parts = Vec::new();
5363                    if let Some(cat) = t.catalog {
5364                        parts.push(cat.name);
5365                    }
5366                    if let Some(schema) = t.schema {
5367                        parts.push(schema.name);
5368                    }
5369                    parts.push(t.name.name);
5370                    let col_name = parts.join(".");
5371                    let alias_expr = if let Some(alias) = t.alias {
5372                        Expression::Alias(Box::new(Alias {
5373                            this: Expression::boxed_column(Column {
5374                                name: Identifier::new(&col_name),
5375                                table: None,
5376                                join_mark: false,
5377                                trailing_comments: Vec::new(),
5378                                span: None,
5379                                inferred_type: None,
5380                            }),
5381                            alias,
5382                            column_aliases: t.column_aliases,
5383                            pre_alias_comments: Vec::new(),
5384                            trailing_comments: t.trailing_comments,
5385                            inferred_type: None,
5386                        }))
5387                    } else {
5388                        Expression::boxed_column(Column {
5389                            name: Identifier::new(&col_name),
5390                            table: None,
5391                            join_mark: false,
5392                            trailing_comments: t.trailing_comments,
5393                            span: None,
5394                            inferred_type: None,
5395                        })
5396                    };
5397                    alias_expr
5398                }
5399                other => other, // Keep as is for non-table expressions
5400            };
5401            expr = Expression::AtIndex(Box::new(AtIndex {
5402                this: Box::new(column_expr),
5403                expression: Box::new(Expression::Identifier(Identifier::new(index_alias))),
5404            }));
5405        }
5406
5407        // Check for TABLESAMPLE/SAMPLE after alias (Snowflake ALIAS_POST_TABLESAMPLE)
5408        // e.g., table2 AS t2 TABLESAMPLE BERNOULLI (50), table2 AS t2 SAMPLE ROW (0)
5409        if self.check(TokenType::TableSample) || self.check(TokenType::Sample) {
5410            if let Some(sample) = self.parse_table_level_sample()? {
5411                // Capture trailing comments after the SAMPLE clause (e.g., -- 25% of rows in table1)
5412                let post_sample_comments = self.previous_trailing_comments().to_vec();
5413                if let Expression::Table(ref mut table) = expr {
5414                    table.table_sample = Some(Box::new(sample));
5415                    if !post_sample_comments.is_empty() {
5416                        table.trailing_comments.extend(post_sample_comments);
5417                    }
5418                } else {
5419                    // For non-Table expressions, wrap in TableSample expression node
5420                    expr = Expression::TableSample(Box::new(crate::expressions::TableSample {
5421                        this: Some(Box::new(expr)),
5422                        sample: Some(Box::new(sample)),
5423                        expressions: Vec::new(),
5424                        method: None,
5425                        bucket_numerator: None,
5426                        bucket_denominator: None,
5427                        bucket_field: None,
5428                        percent: None,
5429                        rows: None,
5430                        size: None,
5431                        seed: None,
5432                    }));
5433                }
5434            }
5435        }
5436
5437        // Apply PostgreSQL ONLY modifier if present
5438        if has_only {
5439            if let Expression::Table(ref mut table) = expr {
5440                table.only = true;
5441            }
5442        }
5443
5444        // BigQuery: FOR SYSTEM_TIME AS OF after alias
5445        // e.g., FROM foo AS t0 FOR SYSTEM_TIME AS OF '2026-01-01'
5446        if self.check(TokenType::For)
5447            && self.current + 1 < self.tokens.len()
5448            && self.tokens[self.current + 1]
5449                .text
5450                .eq_ignore_ascii_case("SYSTEM_TIME")
5451        {
5452            self.skip(); // consume FOR
5453            self.skip(); // consume SYSTEM_TIME
5454            if self.match_token(TokenType::As) && self.check_keyword_text("OF") {
5455                self.skip(); // consume OF
5456                let start = self.current;
5457                // Collect expression tokens until clause boundary
5458                while !self.is_at_end()
5459                    && !self.check(TokenType::Semicolon)
5460                    && !self.check(TokenType::Where)
5461                    && !self.check(TokenType::Join)
5462                    && !self.check(TokenType::Left)
5463                    && !self.check(TokenType::Right)
5464                    && !self.check(TokenType::Inner)
5465                    && !self.check(TokenType::Outer)
5466                    && !self.check(TokenType::Full)
5467                    && !self.check(TokenType::Cross)
5468                    && !self.check(TokenType::Order)
5469                    && !self.check(TokenType::Group)
5470                    && !self.check(TokenType::Having)
5471                    && !self.check(TokenType::Limit)
5472                    && !self.check(TokenType::Union)
5473                    && !self.check(TokenType::Except)
5474                    && !self.check(TokenType::Intersect)
5475                    && !self.check(TokenType::Comma)
5476                    && !self.check(TokenType::RParen)
5477                {
5478                    self.skip();
5479                }
5480                let expr_text = self.tokens_to_sql(start, self.current);
5481                let system_time_str = format!("FOR SYSTEM_TIME AS OF {}", expr_text);
5482                if let Expression::Table(ref mut table) = expr {
5483                    table.system_time = Some(system_time_str);
5484                }
5485            }
5486        }
5487
5488        // BigQuery INFORMATION_SCHEMA handling
5489        // When INFORMATION_SCHEMA is part of a table reference, merge it with the table name
5490        // into a single quoted identifier and auto-add an alias if not present
5491        if matches!(
5492            self.config.dialect,
5493            Some(crate::dialects::DialectType::BigQuery)
5494        ) {
5495            if let Expression::Table(ref mut table) = expr {
5496                // Case 1: Single quoted identifier containing INFORMATION_SCHEMA (e.g., `proj.dataset.INFORMATION_SCHEMA.SOME_VIEW`)
5497                // Add an alias that is the same as the table name (only if no alias)
5498                if table.schema.is_none() && table.catalog.is_none() && table.alias.is_none() {
5499                    let name_upper = table.name.name.to_ascii_uppercase();
5500                    if name_upper.contains("INFORMATION_SCHEMA.") {
5501                        // Set alias to be the full quoted table name
5502                        table.alias = Some(table.name.clone());
5503                        table.alias_explicit_as = true;
5504                    }
5505                }
5506                // Case 2: Multi-part name where schema part is INFORMATION_SCHEMA
5507                // e.g., region_or_dataset.INFORMATION_SCHEMA.TABLES -> region_or_dataset.`INFORMATION_SCHEMA.TABLES` AS TABLES
5508                // e.g., proj.region_or_dataset.INFORMATION_SCHEMA.TABLES -> proj.region_or_dataset.`INFORMATION_SCHEMA.TABLES` AS TABLES
5509                // This applies even if an alias is already set (we still need to merge the parts)
5510                else if let Some(ref schema) = table.schema {
5511                    if schema.name.eq_ignore_ascii_case("INFORMATION_SCHEMA") {
5512                        // Merge schema (INFORMATION_SCHEMA) with table name into a single quoted identifier
5513                        let merged_name = format!("{}.{}", schema.name, table.name.name);
5514                        let original_table_name = table.name.name.clone();
5515
5516                        // Set alias to original table name (TABLES, VIEWS, etc.) only if no alias exists
5517                        if table.alias.is_none() {
5518                            table.alias = Some(Identifier::new(original_table_name));
5519                            table.alias_explicit_as = true;
5520                        }
5521
5522                        // Create new quoted identifier
5523                        table.name = Identifier {
5524                            name: merged_name,
5525                            quoted: true,
5526                            trailing_comments: Vec::new(),
5527                            span: None,
5528                        };
5529
5530                        // Shift: schema becomes catalog, catalog becomes None or stays
5531                        table.schema = table.catalog.take();
5532                        // catalog is now None
5533                    }
5534                }
5535            }
5536        }
5537
5538        Ok(expr)
5539    }
5540
5541    /// Parse standard PIVOT clause (in FROM clause)
5542    /// PIVOT(agg_func [AS alias], ... FOR column IN (value [AS alias], ...) [GROUP BY ...])
5543    fn parse_pivot(&mut self, source: Expression) -> Result<Expression> {
5544        self.expect(TokenType::LParen)?;
5545
5546        // Parse aggregation functions (comma-separated, may have aliases)
5547        // Stop when we see FOR keyword
5548        // Use parse_primary() to handle keyword function names like FIRST, LAST
5549        let mut expressions = Vec::new();
5550        loop {
5551            if self.check(TokenType::For) || self.check(TokenType::RParen) {
5552                break;
5553            }
5554            // Parse the aggregation expression using parse_primary (handles keyword functions)
5555            let func = self.parse_primary()?;
5556            // Check for alias (AS alias or just identifier after function)
5557            let expr = if self.match_token(TokenType::As) {
5558                // AS alias
5559                let alias_name = self.expect_identifier_or_keyword()?;
5560                Expression::Alias(Box::new(Alias::new(func, Identifier::new(alias_name))))
5561            } else if !self.check(TokenType::Comma)
5562                && !self.check(TokenType::For)
5563                && !self.check(TokenType::RParen)
5564            {
5565                // Implicit alias (no AS keyword): SUM(b) d
5566                if let Some(id) = self.parse_id_var()? {
5567                    let alias_name = match &id {
5568                        Expression::Identifier(ident) => ident.name.clone(),
5569                        Expression::Column(col) => col.name.name.clone(),
5570                        _ => String::new(),
5571                    };
5572                    if !alias_name.is_empty() {
5573                        Expression::Alias(Box::new(Alias::new(func, Identifier::new(alias_name))))
5574                    } else {
5575                        func
5576                    }
5577                } else {
5578                    func
5579                }
5580            } else {
5581                func
5582            };
5583            expressions.push(expr);
5584            if !self.match_token(TokenType::Comma) {
5585                break;
5586            }
5587            // After consuming comma, if next is FOR, break (comma before FOR is optional/dropped)
5588            if self.check(TokenType::For) {
5589                break;
5590            }
5591        }
5592
5593        // FOR column IN (values)
5594        self.expect(TokenType::For)?;
5595
5596        let mut fields = Vec::new();
5597        loop {
5598            let field = self.parse_standard_pivot_in()?;
5599            fields.push(field);
5600
5601            // Check for additional FOR clauses (rare but possible)
5602            if !self.match_token(TokenType::For) {
5603                break;
5604            }
5605        }
5606
5607        // Handle Snowflake's DEFAULT ON NULL (default_value) clause
5608        let default_on_null = if self.match_text_seq(&["DEFAULT", "ON", "NULL"]) {
5609            if self.match_token(TokenType::LParen) {
5610                let val = self.parse_expression()?;
5611                self.expect(TokenType::RParen)?;
5612                Some(Box::new(val))
5613            } else {
5614                None
5615            }
5616        } else {
5617            None
5618        };
5619
5620        // Parse optional GROUP BY inside PIVOT parens
5621        let group = self.parse_group()?;
5622
5623        self.expect(TokenType::RParen)?;
5624
5625        Ok(Expression::Pivot(Box::new(Pivot {
5626            this: source,
5627            expressions,
5628            fields,
5629            using: Vec::new(),
5630            group: group.map(Box::new),
5631            unpivot: false,
5632            into: None,
5633            alias: None,
5634            include_nulls: None,
5635            default_on_null,
5636            with: None,
5637        })))
5638    }
5639
5640    /// Parse FOR column IN (...) part of standard PIVOT
5641    fn parse_standard_pivot_in(&mut self) -> Result<Expression> {
5642        // Parse the column being pivoted
5643        let column = self.parse_primary()?;
5644
5645        // IN keyword
5646        self.expect(TokenType::In)?;
5647
5648        // IN values - can be parenthesized or bare identifier
5649        if self.match_token(TokenType::LParen) {
5650            // Check for ANY keyword
5651            let in_exprs = if self.match_text_seq(&["ANY"]) {
5652                let order = self.parse_order()?;
5653                vec![Expression::PivotAny(Box::new(PivotAny {
5654                    this: order.map(Box::new),
5655                }))]
5656            } else {
5657                // Parse comma-separated values with optional aliases
5658                let mut vals = Vec::new();
5659                loop {
5660                    if self.check(TokenType::RParen) {
5661                        break;
5662                    }
5663                    if let Some(val) = self.parse_select_or_expression()? {
5664                        // Check for alias - alias can be an identifier or an expression
5665                        // (e.g., 'PREFIX ' || CHR(38) || ' SUFFIX' in Oracle)
5666                        let val = if self.match_token(TokenType::As) {
5667                            // Parse the alias as an expression (not just an identifier)
5668                            // This allows for string concatenation aliases
5669                            let alias_expr = self.parse_bitwise()?.ok_or_else(|| {
5670                                self.parse_error(
5671                                    "Expected expression after AS in PIVOT/UNPIVOT IN clause",
5672                                )
5673                            })?;
5674                            Expression::PivotAlias(Box::new(PivotAlias {
5675                                this: val,
5676                                alias: alias_expr,
5677                            }))
5678                        } else {
5679                            val
5680                        };
5681                        vals.push(val);
5682                    }
5683                    if !self.match_token(TokenType::Comma) {
5684                        break;
5685                    }
5686                }
5687                vals
5688            };
5689            self.expect(TokenType::RParen)?;
5690            Ok(Expression::In(Box::new(In {
5691                this: column,
5692                expressions: in_exprs,
5693                query: None,
5694                not: false,
5695                global: false,
5696                unnest: None,
5697                is_field: false,
5698            })))
5699        } else {
5700            // Bare identifier: FOR foo IN y_enum (no parentheses)
5701            // Store in query field to distinguish from parenthesized IN
5702            let field_id = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
5703            Ok(Expression::In(Box::new(In {
5704                this: column,
5705                expressions: Vec::new(),
5706                query: Some(field_id),
5707                not: false,
5708                global: false,
5709                unnest: None,
5710                is_field: true,
5711            })))
5712        }
5713    }
5714
5715    /// Parse UNPIVOT clause
5716    /// UNPIVOT (value_column FOR name_column IN (col1, col2, ...))
5717    /// UNPIVOT ((col1, col2) FOR name_column IN (col1, col2, ...))
5718    /// UNPIVOT INCLUDE NULLS (value_column FOR name_column IN (...))
5719    /// UNPIVOT EXCLUDE NULLS (value_column FOR name_column IN (...))
5720    fn parse_unpivot(&mut self, source: Expression) -> Result<Expression> {
5721        // Check for optional INCLUDE NULLS or EXCLUDE NULLS
5722        let include_nulls = if self.match_text_seq(&["INCLUDE", "NULLS"]) {
5723            Some(true)
5724        } else if self.match_text_seq(&["EXCLUDE", "NULLS"]) {
5725            Some(false)
5726        } else {
5727            None
5728        };
5729
5730        self.expect(TokenType::LParen)?;
5731
5732        // Value column(s) - can be identifier or (col1, col2, ...)
5733        // Allow keywords as identifiers (e.g., "values" is a common column name in UNPIVOT)
5734        let (value_column, value_column_parenthesized, extra_value_columns) =
5735            if self.match_token(TokenType::LParen) {
5736                // Parenthesized value column(s)
5737                let col = self.expect_identifier_or_keyword()?;
5738                let mut extra_cols = Vec::new();
5739                while self.match_token(TokenType::Comma) {
5740                    extra_cols.push(Identifier::new(self.expect_identifier_or_keyword()?));
5741                }
5742                self.expect(TokenType::RParen)?;
5743                (Identifier::new(col), true, extra_cols)
5744            } else {
5745                (
5746                    Identifier::new(self.expect_identifier_or_keyword()?),
5747                    false,
5748                    Vec::new(),
5749                )
5750            };
5751
5752        // FOR name_column
5753        self.expect(TokenType::For)?;
5754        let name_column = Identifier::new(self.expect_identifier_or_keyword()?);
5755
5756        // IN (columns with optional aliases)
5757        // Format: col1 [AS alias1], col2 [AS alias2], ...
5758        // Or tuple format: (col1, col2) [AS alias1], (col3, col4) [AS alias2], ...
5759        // Aliases can be expressions like 'PREFIX ' || CHR(38) || ' SUFFIX'
5760        self.expect(TokenType::In)?;
5761        self.expect(TokenType::LParen)?;
5762        let columns = {
5763            let mut cols = Vec::new();
5764            loop {
5765                if self.check(TokenType::RParen) {
5766                    break;
5767                }
5768                // Check if this is a tuple of columns: (col1, col2)
5769                let col_expr = if self.check(TokenType::LParen) {
5770                    // Could be a tuple of columns for multi-value unpivot
5771                    let saved = self.current;
5772                    self.skip(); // consume (
5773                                 // Try parsing as identifier list (tuple of columns)
5774                    let mut tuple_cols = Vec::new();
5775                    let first = self.expect_identifier_or_keyword();
5776                    if let Ok(first_id) = first {
5777                        tuple_cols.push(Expression::column(first_id));
5778                        while self.match_token(TokenType::Comma) {
5779                            if let Ok(id) = self.expect_identifier_or_keyword() {
5780                                tuple_cols.push(Expression::column(id));
5781                            } else {
5782                                break;
5783                            }
5784                        }
5785                        if self.match_token(TokenType::RParen) && tuple_cols.len() > 1 {
5786                            // Successful tuple parse
5787                            Some(Expression::Tuple(Box::new(Tuple {
5788                                expressions: tuple_cols,
5789                            })))
5790                        } else {
5791                            // Not a tuple, backtrack
5792                            self.current = saved;
5793                            self.parse_select_or_expression()?
5794                        }
5795                    } else {
5796                        // Not an identifier, backtrack
5797                        self.current = saved;
5798                        self.parse_select_or_expression()?
5799                    }
5800                } else {
5801                    self.parse_select_or_expression()?
5802                };
5803
5804                if let Some(col) = col_expr {
5805                    // Check for alias
5806                    let col = if self.match_token(TokenType::As) {
5807                        // Parse the alias as an expression (allows string concatenation)
5808                        let alias_expr = self.parse_bitwise()?.ok_or_else(|| {
5809                            self.parse_error("Expected expression after AS in UNPIVOT IN clause")
5810                        })?;
5811                        Expression::PivotAlias(Box::new(PivotAlias {
5812                            this: col,
5813                            alias: alias_expr,
5814                        }))
5815                    } else {
5816                        col
5817                    };
5818                    cols.push(col);
5819                }
5820                if !self.match_token(TokenType::Comma) {
5821                    break;
5822                }
5823            }
5824            cols
5825        };
5826        self.expect(TokenType::RParen)?;
5827
5828        self.expect(TokenType::RParen)?;
5829
5830        Ok(Expression::Unpivot(Box::new(Unpivot {
5831            this: source,
5832            value_column,
5833            name_column,
5834            columns,
5835            alias: None,
5836            value_column_parenthesized,
5837            include_nulls,
5838            extra_value_columns,
5839        })))
5840    }
5841
5842    /// Parse Redshift UNPIVOT in FROM clause for SUPER object traversal
5843    /// Syntax: UNPIVOT expr [AS val_alias AT attr_alias]
5844    /// Examples:
5845    ///   FROM t, UNPIVOT t.arr[0]
5846    ///   FROM t, UNPIVOT t.arr AS val AT attr
5847    fn parse_redshift_unpivot_table(&mut self) -> Result<Expression> {
5848        // Parse the expression (column reference with possible array subscript)
5849        // We need to parse a primary expression that can include:
5850        // - Simple column: c.c_orders
5851        // - Array subscript: c.c_orders[0]
5852        // - Multiple subscripts: c.c_orders[0].items[1]
5853        // Using parse_primary which handles column refs with subscripts
5854        let this = self.parse_primary()?;
5855
5856        // Check for optional AS val_alias AT attr_alias
5857        let alias = if self.match_token(TokenType::As) {
5858            let val_alias = self.expect_identifier_or_keyword()?;
5859            // Check for AT attr_alias
5860            if self.match_text_seq(&["AT"]) {
5861                let attr_alias = self.expect_identifier_or_keyword()?;
5862                // Create alias expression that captures both aliases
5863                // We'll use the val_alias as the main alias and store attr_alias in a way
5864                // the generator can reconstruct "AS val AT attr"
5865                Some(Identifier::new(format!("{} AT {}", val_alias, attr_alias)))
5866            } else {
5867                Some(Identifier::new(val_alias))
5868            }
5869        } else {
5870            None
5871        };
5872
5873        // Return a Pivot expression with unpivot=true
5874        // Use the simplified form pattern where:
5875        // - this: the expression being unpivoted
5876        // - expressions: empty (no ON expressions)
5877        // - unpivot: true
5878        // - alias: captured above
5879        Ok(Expression::Pivot(Box::new(Pivot {
5880            this,
5881            expressions: Vec::new(),
5882            fields: Vec::new(),
5883            using: Vec::new(),
5884            group: None,
5885            unpivot: true,
5886            into: None,
5887            alias,
5888            include_nulls: None,
5889            default_on_null: None,
5890            with: None,
5891        })))
5892    }
5893
5894    /// BigQuery: Parse a table part that may contain hyphens (e.g., project-id)
5895    /// Also handles numeric table parts (e.g., foo.bar.25 -> foo.bar.`25`)
5896    /// Returns the identifier, possibly with merged hyphenated parts and quoted flag set.
5897    fn parse_bigquery_table_part(&mut self) -> Result<Identifier> {
5898        use crate::dialects::DialectType;
5899
5900        // Try to parse a number for BigQuery numeric table parts (e.g., foo.bar.25)
5901        if matches!(self.config.dialect, Some(DialectType::BigQuery))
5902            && self.check(TokenType::Number)
5903        {
5904            let num_token = self.advance().clone();
5905            let mut name = num_token.text.clone();
5906
5907            // Check if followed by more connected tokens (e.g., 25x, 25_, 25ab)
5908            // Numbers followed immediately by identifiers without whitespace are merged
5909            while !self.is_at_end() && self.is_connected() {
5910                let tok = self.advance().clone();
5911                name.push_str(&tok.text);
5912            }
5913
5914            return Ok(Identifier {
5915                name,
5916                quoted: true,
5917                trailing_comments: Vec::new(),
5918                span: None,
5919            });
5920        }
5921
5922        // MySQL numeric-starting identifiers (e.g., 00f, 1d)
5923        if matches!(self.config.dialect, Some(DialectType::MySQL)) && self.check(TokenType::Number)
5924        {
5925            let num_token = self.advance().clone();
5926            let mut name = num_token.text.clone();
5927
5928            // Merge with connected identifier/var tokens only (not punctuation)
5929            while !self.is_at_end()
5930                && self.is_connected()
5931                && (self.check(TokenType::Var) || self.check(TokenType::Identifier))
5932            {
5933                let tok = self.advance().clone();
5934                name.push_str(&tok.text);
5935            }
5936
5937            return Ok(Identifier {
5938                name,
5939                quoted: true,
5940                trailing_comments: Vec::new(),
5941                span: None,
5942            });
5943        }
5944
5945        let mut ident = self.expect_identifier_or_keyword_with_quoted()?;
5946
5947        // BigQuery: merge hyphenated parts (e.g., pro-ject_id -> `pro-ject_id`)
5948        if matches!(self.config.dialect, Some(DialectType::BigQuery)) && !ident.quoted {
5949            // Check if next token is a dash and it looks connected (no space)
5950            if self.check(TokenType::Dash) && self.is_connected_dash() {
5951                let mut name = ident.name.clone();
5952
5953                while self.check(TokenType::Dash) && self.is_connected_dash() {
5954                    self.skip(); // consume dash
5955                    name.push('-');
5956                    // Consume the next part
5957                    let part = self.advance().clone();
5958                    name.push_str(&part.text);
5959                    // Continue consuming connected tokens (for things like a-b-c)
5960                    while !self.is_at_end()
5961                        && self.is_connected()
5962                        && !self.check(TokenType::Dot)
5963                        && !self.check(TokenType::Dash)
5964                        && !self.check(TokenType::LParen)
5965                        && !self.check(TokenType::RParen)
5966                    {
5967                        let tok = self.advance().clone();
5968                        name.push_str(&tok.text);
5969                    }
5970                }
5971
5972                ident = Identifier {
5973                    name,
5974                    quoted: false,
5975                    trailing_comments: Vec::new(),
5976                    span: None,
5977                };
5978            }
5979        }
5980
5981        Ok(ident)
5982    }
5983
5984    /// Check if the current dash token is "connected" to the next token
5985    /// (i.e., the dash and next token are part of a hyphenated identifier)
5986    fn is_connected_dash(&self) -> bool {
5987        if !self.check(TokenType::Dash) {
5988            return false;
5989        }
5990        if self.current + 1 >= self.tokens.len() {
5991            return false;
5992        }
5993        let dash_token = &self.tokens[self.current];
5994        let next_token = &self.tokens[self.current + 1];
5995
5996        // The next token after dash must be an identifier, number, or keyword
5997        // and it must be adjacent (no whitespace between dash and next token)
5998        let next_is_valid = matches!(
5999            next_token.token_type,
6000            TokenType::Identifier
6001                | TokenType::Var
6002                | TokenType::Number
6003                | TokenType::All
6004                | TokenType::Select
6005                | TokenType::From
6006                | TokenType::Where
6007        ) || next_token.token_type.is_keyword();
6008
6009        // Check adjacency: dash ends at dash.end, next starts at next.start
6010        let adjacent = dash_token.span.end + 1 == next_token.span.start
6011            || dash_token.span.end == next_token.span.start;
6012
6013        next_is_valid && adjacent
6014    }
6015
6016    /// Check if the current token is "connected" to the previous token (no whitespace)
6017    fn is_connected(&self) -> bool {
6018        if self.current == 0 || self.current >= self.tokens.len() {
6019            return false;
6020        }
6021        let prev_token = &self.tokens[self.current - 1];
6022        let curr_token = &self.tokens[self.current];
6023        // Tokens are connected if they are immediately adjacent (no characters between them)
6024        // span.end is exclusive, so if prev.end == curr.start, they are adjacent
6025        prev_token.span.end == curr_token.span.start
6026    }
6027
6028    /// Parse a table reference (schema.table format)
6029    fn parse_table_ref(&mut self) -> Result<TableRef> {
6030        // Capture leading comments on the first token (e.g., FROM \n/* comment */\n db.schema.tbl)
6031        let table_ref_leading_comments = self.current_leading_comments().to_vec();
6032        let mut result = self.parse_table_ref_inner()?;
6033        if !table_ref_leading_comments.is_empty() && result.leading_comments.is_empty() {
6034            result.leading_comments = table_ref_leading_comments;
6035        }
6036        Ok(result)
6037    }
6038
6039    fn parse_table_ref_inner(&mut self) -> Result<TableRef> {
6040        // Check for Snowflake IDENTIFIER() function: IDENTIFIER('string') or IDENTIFIER($var)
6041        if self.check_identifier("IDENTIFIER") && self.check_next(TokenType::LParen) {
6042            self.skip(); // consume IDENTIFIER
6043            self.skip(); // consume (
6044                         // Parse the argument: either a string literal, a variable ($foo), or identifier
6045            let arg = if self.check(TokenType::String) {
6046                let s = self.advance().text.clone();
6047                Expression::Literal(Box::new(Literal::String(s)))
6048            } else if self.check(TokenType::Parameter) {
6049                // ?-style parameter
6050                let var = self.advance().text.clone();
6051                Expression::Var(Box::new(crate::expressions::Var { this: var }))
6052            } else if self.check(TokenType::Dollar) {
6053                // $foo style variable - Dollar followed by identifier
6054                self.skip(); // consume $
6055                let var_name = self.expect_identifier()?;
6056                Expression::Var(Box::new(crate::expressions::Var {
6057                    this: format!("${}", var_name),
6058                }))
6059            } else {
6060                // Could be an identifier too
6061                let ident = self.expect_identifier()?;
6062                Expression::Identifier(Identifier::new(ident))
6063            };
6064            self.expect(TokenType::RParen)?;
6065            let trailing_comments = self.previous_trailing_comments().to_vec();
6066            // Create a Function expression to represent IDENTIFIER(arg)
6067            let identifier_func = Expression::Function(Box::new(crate::expressions::Function {
6068                name: "IDENTIFIER".to_string(),
6069                args: vec![arg],
6070                distinct: false,
6071                trailing_comments: Vec::new(),
6072                use_bracket_syntax: false,
6073                no_parens: false,
6074                quoted: false,
6075                span: None,
6076                inferred_type: None,
6077            }));
6078            return Ok(TableRef {
6079                catalog: None,
6080                schema: None,
6081                name: Identifier::empty(),
6082                alias: None,
6083                alias_explicit_as: false,
6084                column_aliases: Vec::new(),
6085                leading_comments: Vec::new(),
6086                trailing_comments,
6087                when: None,
6088                only: false,
6089                final_: false,
6090                table_sample: None,
6091                hints: Vec::new(),
6092                system_time: None,
6093                partitions: Vec::new(),
6094                identifier_func: Some(Box::new(identifier_func)),
6095                changes: None,
6096                version: None,
6097                span: None,
6098            });
6099        }
6100
6101        let first = self.parse_bigquery_table_part()?;
6102
6103        // Check for schema.table format
6104        if self.match_token(TokenType::Dot) {
6105            // Handle TSQL a..b syntax (database..table with empty schema)
6106            if self.check(TokenType::Dot) {
6107                // Two consecutive dots: a..b means catalog..table (empty schema)
6108                self.skip(); // consume second dot
6109                let table = self.parse_bigquery_table_part()?;
6110                let trailing_comments = self.previous_trailing_comments().to_vec();
6111                Ok(TableRef {
6112                    catalog: Some(first),
6113                    schema: Some(Identifier::new("")), // Empty schema represents ..
6114                    name: table,
6115                    alias: None,
6116                    alias_explicit_as: false,
6117                    column_aliases: Vec::new(),
6118                    leading_comments: Vec::new(),
6119                    trailing_comments,
6120                    when: None,
6121                    only: false,
6122                    final_: false,
6123                    table_sample: None,
6124                    hints: Vec::new(),
6125                    system_time: None,
6126                    partitions: Vec::new(),
6127                    identifier_func: None,
6128                    changes: None,
6129                    version: None,
6130                    span: None,
6131                })
6132            } else {
6133                // BigQuery: handle x.* wildcard table reference (e.g., SELECT * FROM x.*)
6134                // After the first dot, if we see a Star token, it's a wildcard table name
6135                if matches!(
6136                    self.config.dialect,
6137                    Some(crate::dialects::DialectType::BigQuery)
6138                ) && self.check(TokenType::Star)
6139                {
6140                    self.skip(); // consume *
6141                    let trailing_comments = self.previous_trailing_comments().to_vec();
6142                    return Ok(TableRef {
6143                        catalog: None,
6144                        schema: Some(first),
6145                        name: Identifier::new("*"),
6146                        alias: None,
6147                        alias_explicit_as: false,
6148                        column_aliases: Vec::new(),
6149                        leading_comments: Vec::new(),
6150                        trailing_comments,
6151                        when: None,
6152                        only: false,
6153                        final_: false,
6154                        table_sample: None,
6155                        hints: Vec::new(),
6156                        system_time: None,
6157                        partitions: Vec::new(),
6158                        identifier_func: None,
6159                        changes: None,
6160                        version: None,
6161                        span: None,
6162                    });
6163                }
6164                let table = self.parse_bigquery_table_part()?;
6165                // Check for catalog.schema.table format
6166                if self.match_token(TokenType::Dot) {
6167                    // BigQuery: handle a.b.* wildcard table reference
6168                    if matches!(
6169                        self.config.dialect,
6170                        Some(crate::dialects::DialectType::BigQuery)
6171                    ) && self.check(TokenType::Star)
6172                    {
6173                        self.skip(); // consume *
6174                        let trailing_comments = self.previous_trailing_comments().to_vec();
6175                        return Ok(TableRef {
6176                            catalog: Some(first),
6177                            schema: Some(table),
6178                            name: Identifier::new("*"),
6179                            alias: None,
6180                            alias_explicit_as: false,
6181                            column_aliases: Vec::new(),
6182                            leading_comments: Vec::new(),
6183                            trailing_comments,
6184                            when: None,
6185                            only: false,
6186                            final_: false,
6187                            table_sample: None,
6188                            hints: Vec::new(),
6189                            system_time: None,
6190                            partitions: Vec::new(),
6191                            identifier_func: None,
6192                            changes: None,
6193                            version: None,
6194                            span: None,
6195                        });
6196                    }
6197                    let actual_table = self.parse_bigquery_table_part()?;
6198                    let trailing_comments = self.previous_trailing_comments().to_vec();
6199                    Ok(TableRef {
6200                        catalog: Some(first),
6201                        schema: Some(table),
6202                        name: actual_table,
6203                        alias: None,
6204                        alias_explicit_as: false,
6205                        column_aliases: Vec::new(),
6206                        leading_comments: Vec::new(),
6207                        trailing_comments,
6208                        when: None,
6209                        only: false,
6210                        final_: false,
6211                        table_sample: None,
6212                        hints: Vec::new(),
6213                        system_time: None,
6214                        partitions: Vec::new(),
6215                        identifier_func: None,
6216                        changes: None,
6217                        version: None,
6218                        span: None,
6219                    })
6220                } else {
6221                    let trailing_comments = self.previous_trailing_comments().to_vec();
6222                    Ok(TableRef {
6223                        catalog: None,
6224                        schema: Some(first),
6225                        name: table,
6226                        alias: None,
6227                        alias_explicit_as: false,
6228                        column_aliases: Vec::new(),
6229                        leading_comments: Vec::new(),
6230                        trailing_comments,
6231                        when: None,
6232                        only: false,
6233                        final_: false,
6234                        table_sample: None,
6235                        hints: Vec::new(),
6236                        system_time: None,
6237                        partitions: Vec::new(),
6238                        identifier_func: None,
6239                        changes: None,
6240                        version: None,
6241                        span: None,
6242                    })
6243                }
6244            }
6245        } else {
6246            let trailing_comments = self.previous_trailing_comments().to_vec();
6247            Ok(TableRef {
6248                catalog: None,
6249                schema: None,
6250                name: first,
6251                alias: None,
6252                alias_explicit_as: false,
6253                column_aliases: Vec::new(),
6254                leading_comments: Vec::new(),
6255                trailing_comments,
6256                when: None,
6257                only: false,
6258                final_: false,
6259                table_sample: None,
6260                hints: Vec::new(),
6261                system_time: None,
6262                partitions: Vec::new(),
6263                identifier_func: None,
6264                changes: None,
6265                version: None,
6266                span: None,
6267            })
6268        }
6269    }
6270
6271    /// Parse a datetime field for EXTRACT function (YEAR, MONTH, DAY, etc.)
6272    fn parse_datetime_field(&mut self) -> Result<DateTimeField> {
6273        let token = self.advance();
6274        let original_name = token.text.clone();
6275        let name = original_name.to_ascii_uppercase();
6276        match name.as_str() {
6277            "YEAR" => Ok(DateTimeField::Year),
6278            "MONTH" => Ok(DateTimeField::Month),
6279            "DAY" => Ok(DateTimeField::Day),
6280            "HOUR" => Ok(DateTimeField::Hour),
6281            "MINUTE" => Ok(DateTimeField::Minute),
6282            "SECOND" => Ok(DateTimeField::Second),
6283            "MILLISECOND" => Ok(DateTimeField::Millisecond),
6284            "MICROSECOND" => Ok(DateTimeField::Microsecond),
6285            "DOW" | "DAYOFWEEK" => Ok(DateTimeField::DayOfWeek),
6286            "DOY" | "DAYOFYEAR" => Ok(DateTimeField::DayOfYear),
6287            "WEEK" => {
6288                // Check for modifier like WEEK(monday)
6289                if self.match_token(TokenType::LParen) {
6290                    let modifier = self.expect_identifier_or_keyword()?;
6291                    self.expect(TokenType::RParen)?;
6292                    Ok(DateTimeField::WeekWithModifier(modifier))
6293                } else {
6294                    Ok(DateTimeField::Week)
6295                }
6296            }
6297            "QUARTER" => Ok(DateTimeField::Quarter),
6298            "EPOCH" => Ok(DateTimeField::Epoch),
6299            "TIMEZONE" => Ok(DateTimeField::Timezone),
6300            "TIMEZONE_HOUR" => Ok(DateTimeField::TimezoneHour),
6301            "TIMEZONE_MINUTE" => Ok(DateTimeField::TimezoneMinute),
6302            "DATE" => Ok(DateTimeField::Date),
6303            "TIME" => Ok(DateTimeField::Time),
6304            // Allow arbitrary field names for dialect-specific functionality
6305            _ => Ok(DateTimeField::Custom(original_name)),
6306        }
6307    }
6308
6309    /// Parse a table expression followed by any joins
6310    /// Used for parenthesized join expressions like (tbl1 CROSS JOIN tbl2)
6311    fn parse_table_expression_with_joins(&mut self) -> Result<(Expression, Vec<Join>)> {
6312        // First parse the left table expression
6313        let left = self.parse_table_expression()?;
6314
6315        // Then parse any joins
6316        let joins = self.parse_joins()?;
6317
6318        Ok((left, joins))
6319    }
6320
6321    /// Parse JOIN clauses
6322    ///
6323    /// Supports right-associative chained JOINs where ON/USING clauses are assigned right-to-left:
6324    /// - `a JOIN b JOIN c ON cond1 ON cond2` means `a JOIN (b JOIN c ON cond1) ON cond2`
6325    /// - The rightmost ON applies to the rightmost unconditioned JOIN
6326    fn parse_joins(&mut self) -> Result<Vec<Join>> {
6327        let mut joins = Vec::with_capacity(2);
6328        let mut nesting_group: usize = 0;
6329
6330        // Loop: Phase 1 (parse JOINs) + Phase 2 (assign deferred conditions)
6331        // After phase 2, if there are more JOIN keywords, continue with another round
6332        loop {
6333            let joins_before = joins.len();
6334
6335            // Phase 1: Parse all JOINs with optional inline ON/USING conditions
6336            loop {
6337                let pos_before_join_kind = self.current;
6338                let join_kind_result = self.try_parse_join_kind();
6339                let (kind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint) =
6340                    match join_kind_result {
6341                        Some(r) => r,
6342                        None => break,
6343                    };
6344                // Collect comments from all tokens consumed by try_parse_join_kind:
6345                // - Leading comments on the first token (comments on a separate line before the join)
6346                // - Trailing comments between join keywords (e.g., INNER /* comment */ JOIN)
6347                let mut join_comments = Vec::new();
6348                // Capture leading comments from the first token of the join kind
6349                if pos_before_join_kind < self.tokens.len() {
6350                    join_comments
6351                        .extend(self.tokens[pos_before_join_kind].comments.iter().cloned());
6352                }
6353                for i in pos_before_join_kind..self.current {
6354                    if i < self.tokens.len() {
6355                        join_comments.extend(self.tokens[i].trailing_comments.iter().cloned());
6356                    }
6357                }
6358                // Snowflake: DIRECTED keyword before JOIN (e.g., CROSS DIRECTED JOIN)
6359                let directed = if needs_join_keyword && self.check_identifier("DIRECTED") {
6360                    self.skip();
6361                    true
6362                } else {
6363                    false
6364                };
6365                if needs_join_keyword {
6366                    self.expect(TokenType::Join)?;
6367                }
6368
6369                // ClickHouse: ARRAY JOIN uses expressions, not table references
6370                let table = if matches!(kind, JoinKind::Array | JoinKind::LeftArray) {
6371                    let mut items = Vec::new();
6372                    // Handle ARRAY JOIN with no arguments (intentional error test)
6373                    if !self.is_at_end()
6374                        && !self.check(TokenType::Semicolon)
6375                        && !self.check(TokenType::RParen)
6376                    {
6377                        loop {
6378                            let expr = self.parse_expression()?;
6379                            let item = if self.match_token(TokenType::As) {
6380                                let alias_name = self.expect_identifier_or_safe_keyword()?;
6381                                Expression::Alias(Box::new(Alias {
6382                                    this: expr,
6383                                    alias: Identifier::new(alias_name),
6384                                    column_aliases: Vec::new(),
6385                                    pre_alias_comments: Vec::new(),
6386                                    trailing_comments: Vec::new(),
6387                                    inferred_type: None,
6388                                }))
6389                            } else {
6390                                expr
6391                            };
6392                            items.push(item);
6393                            if !self.match_token(TokenType::Comma) {
6394                                break;
6395                            }
6396                        }
6397                    } // end if !is_at_end check
6398                    if items.len() == 1 {
6399                        items.pop().unwrap()
6400                    } else if items.is_empty() {
6401                        Expression::Null(Null)
6402                    } else {
6403                        Expression::Tuple(Box::new(Tuple { expressions: items }))
6404                    }
6405                } else {
6406                    self.parse_table_expression()?
6407                };
6408
6409                // Snowflake ASOF JOIN: OFFSET/LIMIT before MATCH_CONDITION are table aliases
6410                let table = if matches!(
6411                    kind,
6412                    JoinKind::AsOf | JoinKind::AsOfLeft | JoinKind::AsOfRight
6413                ) && (self.check(TokenType::Offset) || self.check(TokenType::Limit))
6414                    && self
6415                        .peek_nth(1)
6416                        .map(|t| t.text.eq_ignore_ascii_case("MATCH_CONDITION"))
6417                        == Some(true)
6418                {
6419                    let alias_name = self.advance().text.clone();
6420                    Expression::Alias(Box::new(Alias {
6421                        this: table,
6422                        alias: Identifier::new(alias_name),
6423                        column_aliases: Vec::new(),
6424                        pre_alias_comments: Vec::new(),
6425                        trailing_comments: Vec::new(),
6426                        inferred_type: None,
6427                    }))
6428                } else {
6429                    table
6430                };
6431
6432                // Try to parse inline MATCH_CONDITION/ON/USING (only if not followed by another JOIN)
6433                // We need to peek ahead to see if there's another JOIN keyword coming
6434                let has_match_condition = self.check_identifier("MATCH_CONDITION");
6435                let has_inline_condition = self.check(TokenType::On)
6436                    || self.check(TokenType::Using)
6437                    || has_match_condition;
6438                let next_is_join = self.check_join_keyword();
6439
6440                // Parse MATCH_CONDITION first (Snowflake ASOF JOIN can have MATCH_CONDITION before ON)
6441                let match_condition = if has_match_condition && !next_is_join {
6442                    if self.match_identifier("MATCH_CONDITION") {
6443                        self.expect(TokenType::LParen)?;
6444                        let condition = self.parse_expression()?;
6445                        self.expect(TokenType::RParen)?;
6446                        Some(condition)
6447                    } else {
6448                        None
6449                    }
6450                } else {
6451                    None
6452                };
6453
6454                let (on, using) = if (has_inline_condition || match_condition.is_some())
6455                    && !self.check_join_keyword()
6456                {
6457                    // Parse inline condition only if there's no more JOINs following
6458                    if self.match_token(TokenType::On) {
6459                        (Some(self.parse_expression()?), Vec::new())
6460                    } else if self.match_token(TokenType::Using) {
6461                        // ClickHouse allows USING without parentheses
6462                        let has_parens = self.match_token(TokenType::LParen);
6463                        // Use parse_using_column_list to handle qualified names like t1.col
6464                        let cols = self.parse_using_column_list()?;
6465                        if has_parens {
6466                            self.expect(TokenType::RParen)?;
6467                        }
6468                        (None, cols)
6469                    } else {
6470                        (None, Vec::new())
6471                    }
6472                } else {
6473                    (None, Vec::new())
6474                };
6475
6476                joins.push(Join {
6477                    this: table,
6478                    on,
6479                    using,
6480                    kind,
6481                    use_inner_keyword,
6482                    use_outer_keyword,
6483                    deferred_condition: false,
6484                    join_hint,
6485                    match_condition,
6486                    pivots: Vec::new(),
6487                    comments: join_comments,
6488                    nesting_group,
6489                    directed,
6490                });
6491            }
6492
6493            // Phase 2: Assign deferred ON/USING conditions to unconditioned joins (right-to-left)
6494            // Only consider joins from the current batch (joins_before..)
6495            let unconditioned: Vec<usize> = joins[joins_before..]
6496                .iter()
6497                .enumerate()
6498                .filter(|(_, j)| j.on.is_none() && j.using.is_empty())
6499                .map(|(i, _)| joins_before + i)
6500                .collect();
6501
6502            let mut idx = unconditioned.len();
6503            while idx > 0 {
6504                if self.match_token(TokenType::On) {
6505                    idx -= 1;
6506                    let join_idx = unconditioned[idx];
6507                    joins[join_idx].on = Some(self.parse_expression()?);
6508                    joins[join_idx].deferred_condition = true;
6509                } else if self.match_token(TokenType::Using) {
6510                    idx -= 1;
6511                    let join_idx = unconditioned[idx];
6512                    let has_parens = self.match_token(TokenType::LParen);
6513                    // Handle empty USING ()
6514                    let cols = if has_parens && self.check(TokenType::RParen) {
6515                        Vec::new()
6516                    } else {
6517                        // Use parse_using_column_list to handle qualified names like t1.col
6518                        self.parse_using_column_list()?
6519                    };
6520                    joins[join_idx].using = cols;
6521                    if has_parens {
6522                        self.expect(TokenType::RParen)?;
6523                    }
6524                    joins[join_idx].deferred_condition = true;
6525                } else {
6526                    break;
6527                }
6528            }
6529
6530            // If no new joins were parsed in this round, we're done
6531            if joins.len() == joins_before {
6532                break;
6533            }
6534
6535            // If there are more JOIN keywords after deferred conditions, continue with another round
6536            if !self.check_join_keyword() {
6537                break;
6538            }
6539            nesting_group += 1;
6540        }
6541
6542        Ok(joins)
6543    }
6544
6545    /// Check if the current token starts a JOIN clause
6546    fn check_join_keyword(&self) -> bool {
6547        self.check(TokenType::Join) ||
6548        self.check(TokenType::Inner) ||
6549        self.check(TokenType::Left) ||
6550        self.check(TokenType::Right) ||
6551        self.check(TokenType::Full) ||
6552        self.check(TokenType::Cross) ||
6553        self.check(TokenType::Natural) ||
6554        self.check(TokenType::Outer) ||
6555        // ClickHouse: ARRAY JOIN, GLOBAL JOIN, ALL JOIN, ANY JOIN, PASTE JOIN
6556        (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) &&
6557            (self.check_identifier("ARRAY") || self.check_identifier("GLOBAL") || self.check(TokenType::All) || self.check(TokenType::Any) || self.check_identifier("PASTE")))
6558    }
6559
6560    /// Try to parse a JOIN kind
6561    /// Returns (JoinKind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint)
6562    fn try_parse_join_kind(&mut self) -> Option<(JoinKind, bool, bool, bool, Option<String>)> {
6563        if matches!(
6564            self.config.dialect,
6565            Some(crate::dialects::DialectType::ClickHouse)
6566        ) {
6567            let start = self.current;
6568            let mut global = false;
6569            let mut strictness: Option<String> = None;
6570            let mut kind: Option<JoinKind> = None;
6571            let mut use_outer = false;
6572            let mut use_inner = false;
6573
6574            if self.match_identifier("GLOBAL") {
6575                global = true;
6576            }
6577
6578            loop {
6579                if strictness.is_none() && self.match_token(TokenType::All) {
6580                    strictness = Some("ALL".to_string());
6581                    continue;
6582                }
6583                if strictness.is_none() && self.match_token(TokenType::Any) {
6584                    strictness = Some("ANY".to_string());
6585                    continue;
6586                }
6587                if strictness.is_none() && self.match_token(TokenType::AsOf) {
6588                    strictness = Some("ASOF".to_string());
6589                    continue;
6590                }
6591                if strictness.is_none() && self.match_token(TokenType::Semi) {
6592                    strictness = Some("SEMI".to_string());
6593                    continue;
6594                }
6595                if strictness.is_none() && self.match_token(TokenType::Anti) {
6596                    strictness = Some("ANTI".to_string());
6597                    continue;
6598                }
6599                if kind.is_none() && self.match_token(TokenType::Left) {
6600                    use_outer = self.match_token(TokenType::Outer);
6601                    use_inner = self.match_token(TokenType::Inner);
6602                    kind = Some(JoinKind::Left);
6603                    continue;
6604                }
6605                if kind.is_none() && self.match_token(TokenType::Right) {
6606                    use_outer = self.match_token(TokenType::Outer);
6607                    use_inner = self.match_token(TokenType::Inner);
6608                    kind = Some(JoinKind::Right);
6609                    continue;
6610                }
6611                if kind.is_none() && self.match_token(TokenType::Full) {
6612                    use_outer = self.match_token(TokenType::Outer);
6613                    kind = Some(JoinKind::Full);
6614                    continue;
6615                }
6616                if kind.is_none() && self.match_token(TokenType::Inner) {
6617                    use_inner = true;
6618                    kind = Some(JoinKind::Inner);
6619                    continue;
6620                }
6621                break;
6622            }
6623
6624            // ClickHouse: ARRAY JOIN or LEFT ARRAY JOIN
6625            if self.check_identifier("ARRAY") && self.check_next(TokenType::Join) {
6626                let array_kind = if matches!(kind, Some(JoinKind::Left)) {
6627                    JoinKind::LeftArray
6628                } else {
6629                    JoinKind::Array
6630                };
6631                self.skip(); // consume ARRAY
6632                             // JOIN will be consumed by caller
6633                return Some((array_kind, true, false, false, None));
6634            }
6635
6636            // ClickHouse: PASTE JOIN (positional join, no ON/USING)
6637            if self.check_identifier("PASTE") && self.check_next(TokenType::Join) {
6638                self.skip(); // consume PASTE
6639                             // JOIN will be consumed by caller
6640                return Some((JoinKind::Paste, true, false, false, None));
6641            }
6642
6643            if global || strictness.is_some() || kind.is_some() {
6644                if self.check(TokenType::Join) {
6645                    let join_kind = kind.unwrap_or(JoinKind::Inner);
6646                    let mut hints = Vec::new();
6647                    if global {
6648                        hints.push("GLOBAL".to_string());
6649                    }
6650                    if let Some(strict) = strictness {
6651                        hints.push(strict);
6652                    }
6653                    let join_hint = if hints.is_empty() {
6654                        None
6655                    } else {
6656                        Some(hints.join(" "))
6657                    };
6658                    return Some((join_kind, true, use_inner, use_outer, join_hint));
6659                } else {
6660                    self.current = start;
6661                }
6662            }
6663        }
6664
6665        // Check for ASOF first (DuckDB/Snowflake) - can be followed by LEFT/RIGHT/etc.
6666        if self.match_token(TokenType::AsOf) {
6667            // ASOF can be followed by LEFT, RIGHT, INNER, or standalone
6668            if self.match_token(TokenType::Left) {
6669                let use_outer = self.match_token(TokenType::Outer);
6670                Some((JoinKind::AsOfLeft, true, false, use_outer, None))
6671            } else if self.match_token(TokenType::Right) {
6672                let use_outer = self.match_token(TokenType::Outer);
6673                Some((JoinKind::AsOfRight, true, false, use_outer, None))
6674            } else if self.match_token(TokenType::Inner) {
6675                Some((JoinKind::AsOf, true, true, false, None))
6676            } else {
6677                // Standalone ASOF JOIN
6678                Some((JoinKind::AsOf, true, false, false, None))
6679            }
6680        } else if self.check(TokenType::Inner) {
6681            // Check if INNER is followed by a set operation (BigQuery INNER UNION/INTERSECT/EXCEPT)
6682            // In that case, don't treat it as a JOIN keyword
6683            let saved = self.current;
6684            self.skip(); // consume INNER
6685            if self.check(TokenType::Union)
6686                || self.check(TokenType::Intersect)
6687                || self.check(TokenType::Except)
6688            {
6689                self.current = saved; // backtrack
6690                return None;
6691            }
6692            // Check for TSQL join hints: INNER LOOP JOIN, INNER HASH JOIN, INNER MERGE JOIN
6693            let join_hint = self.parse_tsql_join_hint();
6694            Some((JoinKind::Inner, true, true, false, join_hint)) // INNER keyword was explicit
6695        } else if self.check(TokenType::Left) {
6696            // Check if LEFT is followed by a set operation (BigQuery LEFT UNION/INTERSECT/EXCEPT)
6697            let saved = self.current;
6698            self.skip(); // consume LEFT
6699                         // LEFT can be followed by OUTER/INNER then set op, or directly by set op
6700            let at_set_op = self.check(TokenType::Union)
6701                || self.check(TokenType::Intersect)
6702                || self.check(TokenType::Except);
6703            let at_inner_set_op = self.check(TokenType::Inner) && {
6704                let saved2 = self.current;
6705                self.skip();
6706                let is_setop = self.check(TokenType::Union)
6707                    || self.check(TokenType::Intersect)
6708                    || self.check(TokenType::Except);
6709                self.current = saved2;
6710                is_setop
6711            };
6712            if at_set_op || at_inner_set_op {
6713                self.current = saved; // backtrack
6714                return None;
6715            }
6716            // Continue with normal LEFT JOIN parsing
6717            self.current = saved;
6718            self.match_token(TokenType::Left); // re-consume LEFT
6719            let use_outer = self.match_token(TokenType::Outer);
6720            let use_inner = self.match_token(TokenType::Inner);
6721            let join_hint = self.parse_tsql_join_hint();
6722            // Check for SEMI, ANTI, or LATERAL
6723            if self.match_token(TokenType::Semi) {
6724                Some((JoinKind::LeftSemi, true, use_inner, use_outer, join_hint))
6725            } else if self.match_token(TokenType::Anti) {
6726                Some((JoinKind::LeftAnti, true, use_inner, use_outer, join_hint))
6727            } else if self.match_token(TokenType::Lateral) {
6728                Some((JoinKind::LeftLateral, true, use_inner, use_outer, join_hint))
6729            } else {
6730                Some((JoinKind::Left, true, use_inner, use_outer, join_hint))
6731            }
6732        } else if self.check(TokenType::Right) {
6733            // Check if RIGHT is followed by a set operation (BigQuery RIGHT UNION/INTERSECT/EXCEPT)
6734            let saved = self.current;
6735            self.skip(); // consume RIGHT
6736            let at_set_op = self.check(TokenType::Union)
6737                || self.check(TokenType::Intersect)
6738                || self.check(TokenType::Except);
6739            let at_inner_set_op = self.check(TokenType::Inner) && {
6740                let saved2 = self.current;
6741                self.skip();
6742                let is_setop = self.check(TokenType::Union)
6743                    || self.check(TokenType::Intersect)
6744                    || self.check(TokenType::Except);
6745                self.current = saved2;
6746                is_setop
6747            };
6748            if at_set_op || at_inner_set_op {
6749                self.current = saved; // backtrack
6750                return None;
6751            }
6752            // Continue with normal RIGHT JOIN parsing
6753            self.current = saved;
6754            self.match_token(TokenType::Right); // re-consume RIGHT
6755            let use_outer = self.match_token(TokenType::Outer);
6756            let use_inner = self.match_token(TokenType::Inner);
6757            let join_hint = self.parse_tsql_join_hint();
6758            // Check for SEMI or ANTI
6759            if self.match_token(TokenType::Semi) {
6760                Some((JoinKind::RightSemi, true, use_inner, use_outer, join_hint))
6761            } else if self.match_token(TokenType::Anti) {
6762                Some((JoinKind::RightAnti, true, use_inner, use_outer, join_hint))
6763            } else {
6764                Some((JoinKind::Right, true, use_inner, use_outer, join_hint))
6765            }
6766        } else if self.check(TokenType::Full) {
6767            // Check if FULL is followed by a set operation (BigQuery FULL UNION/INTERSECT/EXCEPT)
6768            let saved = self.current;
6769            self.skip(); // consume FULL
6770            let at_set_op = self.check(TokenType::Union)
6771                || self.check(TokenType::Intersect)
6772                || self.check(TokenType::Except);
6773            let at_inner_set_op = self.check(TokenType::Inner) && {
6774                let saved2 = self.current;
6775                self.skip();
6776                let is_setop = self.check(TokenType::Union)
6777                    || self.check(TokenType::Intersect)
6778                    || self.check(TokenType::Except);
6779                self.current = saved2;
6780                is_setop
6781            };
6782            if at_set_op || at_inner_set_op {
6783                self.current = saved; // backtrack
6784                return None;
6785            }
6786            // Continue with normal FULL JOIN parsing
6787            self.current = saved;
6788            self.match_token(TokenType::Full); // re-consume FULL
6789            let use_outer = self.match_token(TokenType::Outer);
6790            let join_hint = self.parse_tsql_join_hint();
6791            Some((JoinKind::Full, true, false, use_outer, join_hint))
6792        } else if self.match_token(TokenType::Cross) {
6793            // CROSS JOIN or CROSS APPLY
6794            if self.match_token(TokenType::Apply) {
6795                Some((JoinKind::CrossApply, false, false, false, None))
6796            } else {
6797                Some((JoinKind::Cross, true, false, false, None))
6798            }
6799        } else if self.match_token(TokenType::Natural) {
6800            // NATURAL can be followed by LEFT, RIGHT, INNER, FULL, or just JOIN
6801            if self.match_token(TokenType::Left) {
6802                let use_outer = self.match_token(TokenType::Outer);
6803                Some((JoinKind::NaturalLeft, true, false, use_outer, None))
6804            } else if self.match_token(TokenType::Right) {
6805                let use_outer = self.match_token(TokenType::Outer);
6806                Some((JoinKind::NaturalRight, true, false, use_outer, None))
6807            } else if self.match_token(TokenType::Full) {
6808                let use_outer = self.match_token(TokenType::Outer);
6809                Some((JoinKind::NaturalFull, true, false, use_outer, None))
6810            } else if self.match_token(TokenType::Inner) {
6811                Some((JoinKind::Natural, true, true, false, None))
6812            } else {
6813                Some((JoinKind::Natural, true, false, false, None))
6814            }
6815        } else if self.match_token(TokenType::Outer) {
6816            // OUTER APPLY or standalone OUTER JOIN
6817            if self.match_token(TokenType::Apply) {
6818                Some((JoinKind::OuterApply, false, false, true, None))
6819            } else {
6820                // Standalone OUTER JOIN (without LEFT/RIGHT/FULL)
6821                Some((JoinKind::Outer, true, false, true, None))
6822            }
6823        } else if self.check(TokenType::Lateral) {
6824            // Check if this is LATERAL VIEW (Hive/Spark syntax) vs LATERAL JOIN
6825            if self.current + 1 < self.tokens.len()
6826                && self.tokens[self.current + 1].token_type == TokenType::View
6827            {
6828                // LATERAL VIEW is not a JOIN type, return None
6829                None
6830            } else {
6831                self.skip(); // Consume LATERAL
6832                Some((JoinKind::Lateral, true, false, false, None))
6833            }
6834        } else if self.match_token(TokenType::Semi) {
6835            Some((JoinKind::Semi, true, false, false, None))
6836        } else if self.match_token(TokenType::Anti) {
6837            Some((JoinKind::Anti, true, false, false, None))
6838        } else if self.check_identifier("POSITIONAL") && self.check_next(TokenType::Join) {
6839            // DuckDB POSITIONAL JOIN
6840            self.skip(); // consume POSITIONAL
6841            Some((JoinKind::Positional, true, false, false, None))
6842        } else if self.match_token(TokenType::StraightJoin) {
6843            // STRAIGHT_JOIN in MySQL - doesn't need JOIN keyword after it
6844            Some((JoinKind::Straight, false, false, false, None))
6845        } else if self.check(TokenType::Join) {
6846            Some((JoinKind::Inner, true, false, false, None)) // Default JOIN is INNER (without explicit INNER keyword)
6847        } else if self.match_token(TokenType::Comma) {
6848            // Comma-separated tables: FROM a, b (old-style ANSI join syntax)
6849            Some((JoinKind::Implicit, false, false, false, None)) // No JOIN keyword needed
6850        } else {
6851            None
6852        }
6853    }
6854
6855    /// Parse TSQL join hints: LOOP, HASH, MERGE, REMOTE
6856    fn parse_tsql_join_hint(&mut self) -> Option<String> {
6857        if self.check_identifier("LOOP") {
6858            self.skip();
6859            Some("LOOP".to_string())
6860        } else if self.check_identifier("HASH") {
6861            self.skip();
6862            Some("HASH".to_string())
6863        } else if self.check_identifier("REMOTE") {
6864            self.skip();
6865            Some("REMOTE".to_string())
6866        } else if self.check(TokenType::Merge) && {
6867            // Be careful: MERGE is also a keyword for MERGE statement
6868            // Only treat as hint if followed by JOIN
6869            let next_pos = self.current + 1;
6870            next_pos < self.tokens.len() && self.tokens[next_pos].token_type == TokenType::Join
6871        } {
6872            self.skip();
6873            Some("MERGE".to_string())
6874        } else {
6875            None
6876        }
6877    }
6878
6879    /// Parse GROUP BY clause
6880    fn parse_group_by(&mut self) -> Result<GroupBy> {
6881        // Check for optional ALL/DISTINCT modifier
6882        // Some(true) = ALL, Some(false) = DISTINCT, None = no modifier
6883        let all = if self.match_token(TokenType::All) {
6884            Some(true)
6885        } else if self.match_token(TokenType::Distinct) {
6886            Some(false)
6887        } else {
6888            None
6889        };
6890
6891        let mut expressions = Vec::new();
6892
6893        // GROUP BY ALL / GROUP BY DISTINCT without following CUBE/ROLLUP/expressions
6894        // should return early (e.g., Snowflake's "GROUP BY ALL" without column list).
6895        // But in Presto/Trino, ALL/DISTINCT can be followed by CUBE/ROLLUP expressions.
6896        if all.is_some() && self.is_at_query_modifier_or_end() {
6897            return Ok(GroupBy {
6898                expressions,
6899                all,
6900                totals: false,
6901                comments: Vec::new(),
6902            });
6903        }
6904
6905        // GROUP BY ALL WITH ROLLUP/CUBE/TOTALS — skip expression parsing, go straight to modifiers
6906        if all.is_some()
6907            && self.check(TokenType::With)
6908            && (self.check_next(TokenType::Cube)
6909                || self.check_next(TokenType::Rollup)
6910                || self.check_next_identifier("TOTALS"))
6911        {
6912            let mut totals = false;
6913            // Process WITH ROLLUP/CUBE
6914            if self.check_next(TokenType::Cube) || self.check_next(TokenType::Rollup) {
6915                self.skip(); // consume WITH
6916                if self.match_token(TokenType::Cube) {
6917                    expressions.push(Expression::Cube(Box::new(Cube {
6918                        expressions: Vec::new(),
6919                    })));
6920                } else if self.match_token(TokenType::Rollup) {
6921                    expressions.push(Expression::Rollup(Box::new(Rollup {
6922                        expressions: Vec::new(),
6923                    })));
6924                }
6925            }
6926            // Check for WITH TOTALS (possibly chained after ROLLUP/CUBE)
6927            if self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
6928                self.skip(); // WITH
6929                self.skip(); // TOTALS
6930                totals = true;
6931            }
6932            return Ok(GroupBy {
6933                expressions,
6934                all,
6935                totals,
6936                comments: Vec::new(),
6937            });
6938        }
6939
6940        loop {
6941            // Check for GROUPING SETS, CUBE, ROLLUP
6942            let expr = if self.check_identifier("GROUPING")
6943                && self
6944                    .peek_nth(1)
6945                    .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS"))
6946                && {
6947                    self.skip();
6948                    self.skip();
6949                    true
6950                } {
6951                // GROUPING SETS (...)
6952                self.expect(TokenType::LParen)?;
6953                let args = self.parse_grouping_sets_args()?;
6954                self.expect(TokenType::RParen)?;
6955                Expression::Function(Box::new(Function {
6956                    name: "GROUPING SETS".to_string(),
6957                    args,
6958                    distinct: false,
6959                    trailing_comments: Vec::new(),
6960                    use_bracket_syntax: false,
6961                    no_parens: false,
6962                    quoted: false,
6963                    span: None,
6964                    inferred_type: None,
6965                }))
6966            } else if self.match_token(TokenType::Cube) {
6967                // CUBE (...)
6968                self.expect(TokenType::LParen)?;
6969                let args = self.parse_expression_list()?;
6970                self.expect(TokenType::RParen)?;
6971                Expression::Function(Box::new(Function {
6972                    name: "CUBE".to_string(),
6973                    args,
6974                    distinct: false,
6975                    trailing_comments: Vec::new(),
6976                    use_bracket_syntax: false,
6977                    no_parens: false,
6978                    quoted: false,
6979                    span: None,
6980                    inferred_type: None,
6981                }))
6982            } else if self.match_token(TokenType::Rollup) {
6983                // ROLLUP (...)
6984                self.expect(TokenType::LParen)?;
6985                let args = self.parse_expression_list()?;
6986                self.expect(TokenType::RParen)?;
6987                Expression::Function(Box::new(Function {
6988                    name: "ROLLUP".to_string(),
6989                    args,
6990                    distinct: false,
6991                    trailing_comments: Vec::new(),
6992                    use_bracket_syntax: false,
6993                    no_parens: false,
6994                    quoted: false,
6995                    span: None,
6996                    inferred_type: None,
6997                }))
6998            } else {
6999                self.parse_expression()?
7000            };
7001
7002            // ClickHouse: GROUP BY expr AS alias
7003            let expr = if matches!(
7004                self.config.dialect,
7005                Some(crate::dialects::DialectType::ClickHouse)
7006            ) && self.check(TokenType::As)
7007                && !self.check_next(TokenType::LParen)
7008            {
7009                self.skip(); // consume AS
7010                let alias = self.expect_identifier_or_keyword_with_quoted()?;
7011                Expression::Alias(Box::new(Alias::new(expr, alias)))
7012            } else {
7013                expr
7014            };
7015
7016            expressions.push(expr);
7017
7018            if !self.match_token(TokenType::Comma) {
7019                // Allow adjacent CUBE/ROLLUP/GROUPING SETS without comma separator
7020                // e.g., GROUP BY CUBE(a) ROLLUP(b), GROUPING SETS((c, d))
7021                if self.check(TokenType::Cube)
7022                    || self.check(TokenType::Rollup)
7023                    || (self.check_identifier("GROUPING")
7024                        && self
7025                            .peek_nth(1)
7026                            .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS")))
7027                {
7028                    continue;
7029                }
7030                break;
7031            }
7032        }
7033
7034        // Check for trailing WITH CUBE or WITH ROLLUP (Hive/MySQL syntax)
7035        // This is different from CUBE(...) or ROLLUP(...) which are parsed inline above
7036        // Use lookahead to avoid consuming WITH if it's not followed by CUBE or ROLLUP
7037        // (e.g., Redshift's WITH NO SCHEMA BINDING should not be consumed here)
7038        if self.check(TokenType::With)
7039            && (self.check_next(TokenType::Cube) || self.check_next(TokenType::Rollup))
7040        {
7041            self.skip(); // consume WITH
7042            if self.match_token(TokenType::Cube) {
7043                // WITH CUBE - add Cube with empty expressions
7044                expressions.push(Expression::Cube(Box::new(Cube {
7045                    expressions: Vec::new(),
7046                })));
7047            } else if self.match_token(TokenType::Rollup) {
7048                // WITH ROLLUP - add Rollup with empty expressions
7049                expressions.push(Expression::Rollup(Box::new(Rollup {
7050                    expressions: Vec::new(),
7051                })));
7052            }
7053        }
7054
7055        // ClickHouse: WITH TOTALS
7056        let totals = if self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
7057            self.skip(); // consume WITH
7058            self.skip(); // consume TOTALS
7059            true
7060        } else {
7061            false
7062        };
7063
7064        Ok(GroupBy {
7065            expressions,
7066            all,
7067            totals,
7068            comments: Vec::new(),
7069        })
7070    }
7071
7072    /// Parse GROUPING SETS arguments which can include tuples like (x, y), nested GROUPING SETS, CUBE, ROLLUP
7073    fn parse_grouping_sets_args(&mut self) -> Result<Vec<Expression>> {
7074        let mut args = Vec::new();
7075
7076        loop {
7077            // Check for nested GROUPING SETS, CUBE, ROLLUP
7078            let expr = if self.check_identifier("GROUPING")
7079                && self
7080                    .peek_nth(1)
7081                    .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS"))
7082                && {
7083                    self.skip();
7084                    self.skip();
7085                    true
7086                } {
7087                // Nested GROUPING SETS (...)
7088                self.expect(TokenType::LParen)?;
7089                let inner_args = self.parse_grouping_sets_args()?;
7090                self.expect(TokenType::RParen)?;
7091                Expression::Function(Box::new(Function {
7092                    name: "GROUPING SETS".to_string(),
7093                    args: inner_args,
7094                    distinct: false,
7095                    trailing_comments: Vec::new(),
7096                    use_bracket_syntax: false,
7097                    no_parens: false,
7098                    quoted: false,
7099                    span: None,
7100                    inferred_type: None,
7101                }))
7102            } else if self.match_token(TokenType::Cube) {
7103                // CUBE (...)
7104                self.expect(TokenType::LParen)?;
7105                let inner_args = self.parse_expression_list()?;
7106                self.expect(TokenType::RParen)?;
7107                Expression::Function(Box::new(Function {
7108                    name: "CUBE".to_string(),
7109                    args: inner_args,
7110                    distinct: false,
7111                    trailing_comments: Vec::new(),
7112                    use_bracket_syntax: false,
7113                    no_parens: false,
7114                    quoted: false,
7115                    span: None,
7116                    inferred_type: None,
7117                }))
7118            } else if self.match_token(TokenType::Rollup) {
7119                // ROLLUP (...)
7120                self.expect(TokenType::LParen)?;
7121                let inner_args = self.parse_expression_list()?;
7122                self.expect(TokenType::RParen)?;
7123                Expression::Function(Box::new(Function {
7124                    name: "ROLLUP".to_string(),
7125                    args: inner_args,
7126                    distinct: false,
7127                    trailing_comments: Vec::new(),
7128                    use_bracket_syntax: false,
7129                    no_parens: false,
7130                    quoted: false,
7131                    span: None,
7132                    inferred_type: None,
7133                }))
7134            } else if self.check(TokenType::LParen) {
7135                // This could be a tuple like (x, y) or empty ()
7136                self.skip(); // consume (
7137                if self.check(TokenType::RParen) {
7138                    // Empty tuple ()
7139                    self.skip();
7140                    Expression::Tuple(Box::new(Tuple {
7141                        expressions: Vec::new(),
7142                    }))
7143                } else {
7144                    let inner = self.parse_expression_list()?;
7145                    self.expect(TokenType::RParen)?;
7146                    Expression::Tuple(Box::new(Tuple { expressions: inner }))
7147                }
7148            } else {
7149                self.parse_expression()?
7150            };
7151
7152            args.push(expr);
7153
7154            if !self.match_token(TokenType::Comma) {
7155                break;
7156            }
7157        }
7158
7159        Ok(args)
7160    }
7161
7162    /// Parse ORDER BY clause
7163    fn parse_order_by(&mut self) -> Result<OrderBy> {
7164        self.parse_order_by_with_siblings(false)
7165    }
7166
7167    /// Parse ORDER BY clause with optional siblings flag (Oracle ORDER SIBLINGS BY)
7168    fn parse_order_by_with_siblings(&mut self, siblings: bool) -> Result<OrderBy> {
7169        let mut expressions = Vec::new();
7170
7171        loop {
7172            let expr = self.parse_expression()?;
7173
7174            // ClickHouse: ORDER BY expr AS alias — allow AS alias before DESC/ASC
7175            // But NOT AS SELECT/WITH which would be CREATE TABLE ... AS SELECT
7176            let expr = if matches!(
7177                self.config.dialect,
7178                Some(crate::dialects::DialectType::ClickHouse)
7179            ) && self.check(TokenType::As)
7180                && !self.check_next(TokenType::LParen)
7181                && !self.check_next(TokenType::Select)
7182                && !self.check_next(TokenType::With)
7183            {
7184                self.skip(); // consume AS
7185                let alias = self.expect_identifier_or_keyword_with_quoted()?;
7186                Expression::Alias(Box::new(Alias::new(expr, alias)))
7187            } else {
7188                expr
7189            };
7190
7191            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7192                (true, false)
7193            } else if self.match_token(TokenType::Asc) {
7194                (false, true)
7195            } else {
7196                (false, false)
7197            };
7198
7199            let nulls_first = if self.match_token(TokenType::Nulls) {
7200                if self.match_token(TokenType::First) {
7201                    Some(true)
7202                } else if self.match_token(TokenType::Last) {
7203                    Some(false)
7204                } else {
7205                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7206                }
7207            } else {
7208                None
7209            };
7210
7211            // Parse optional WITH FILL clause (ClickHouse)
7212            let with_fill = if self.match_text_seq(&["WITH", "FILL"]) {
7213                let from_ = if self.match_token(TokenType::From) {
7214                    Some(Box::new(self.parse_or()?))
7215                } else {
7216                    None
7217                };
7218                let to = if self.match_text_seq(&["TO"]) {
7219                    Some(Box::new(self.parse_or()?))
7220                } else {
7221                    None
7222                };
7223                let step = if self.match_text_seq(&["STEP"]) {
7224                    Some(Box::new(self.parse_or()?))
7225                } else {
7226                    None
7227                };
7228                // ClickHouse: STALENESS [INTERVAL] expr
7229                let staleness = if self.match_text_seq(&["STALENESS"]) {
7230                    Some(Box::new(self.parse_or()?))
7231                } else {
7232                    None
7233                };
7234                let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
7235                    if self.match_token(TokenType::LParen) {
7236                        // Parse INTERPOLATE items: identifier [AS expression], ...
7237                        let mut items = Vec::new();
7238                        loop {
7239                            if self.check(TokenType::RParen) {
7240                                break;
7241                            }
7242                            let quoted = self.check(TokenType::QuotedIdentifier);
7243                            let name_text = self.expect_identifier_or_safe_keyword()?;
7244                            let name_id = Identifier {
7245                                name: name_text,
7246                                quoted,
7247                                trailing_comments: Vec::new(),
7248                                span: None,
7249                            };
7250                            let item = if self.match_token(TokenType::As) {
7251                                let expr = self.parse_expression()?;
7252                                // Store as Alias: this=expression, alias=name
7253                                Expression::Alias(Box::new(Alias {
7254                                    this: expr,
7255                                    alias: name_id,
7256                                    column_aliases: Vec::new(),
7257                                    pre_alias_comments: Vec::new(),
7258                                    trailing_comments: Vec::new(),
7259                                    inferred_type: None,
7260                                }))
7261                            } else {
7262                                Expression::Identifier(name_id)
7263                            };
7264                            items.push(item);
7265                            if !self.match_token(TokenType::Comma) {
7266                                break;
7267                            }
7268                        }
7269                        self.expect(TokenType::RParen)?;
7270                        if items.len() == 1 {
7271                            Some(Box::new(items.into_iter().next().unwrap()))
7272                        } else {
7273                            Some(Box::new(Expression::Tuple(Box::new(
7274                                crate::expressions::Tuple { expressions: items },
7275                            ))))
7276                        }
7277                    } else {
7278                        None
7279                    }
7280                } else {
7281                    None
7282                };
7283                Some(Box::new(WithFill {
7284                    from_,
7285                    to,
7286                    step,
7287                    staleness,
7288                    interpolate,
7289                }))
7290            } else {
7291                None
7292            };
7293
7294            expressions.push(Ordered {
7295                this: expr,
7296                desc,
7297                nulls_first,
7298                explicit_asc,
7299                with_fill,
7300            });
7301
7302            if !self.match_token(TokenType::Comma) {
7303                break;
7304            }
7305
7306            // Handle trailing comma: if at end of input or semicolon, break
7307            if self.is_at_end() || self.check(TokenType::Semicolon) {
7308                break;
7309            }
7310        }
7311
7312        Ok(OrderBy {
7313            expressions,
7314            siblings,
7315            comments: Vec::new(),
7316        })
7317    }
7318
7319    /// Parse query modifiers (ORDER BY, LIMIT, OFFSET, DISTRIBUTE BY, SORT BY, CLUSTER BY) for parenthesized queries
7320    /// e.g., (SELECT 1) ORDER BY x LIMIT 1 OFFSET 1
7321    /// e.g., (SELECT 1 UNION SELECT 2) DISTRIBUTE BY z SORT BY x
7322    fn parse_query_modifiers(&mut self, inner: Expression) -> Result<Expression> {
7323        // Parse DISTRIBUTE BY (Hive/Spark)
7324        let distribute_by = if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
7325            let exprs = self.parse_expression_list()?;
7326            Some(DistributeBy { expressions: exprs })
7327        } else {
7328            None
7329        };
7330
7331        // Parse SORT BY (Hive/Spark) or CLUSTER BY (Hive/Spark)
7332        let (sort_by, cluster_by) = if self.match_keywords(&[TokenType::Sort, TokenType::By]) {
7333            // SORT BY
7334            let mut orders = Vec::new();
7335            loop {
7336                if let Some(ordered) = self.parse_ordered_item()? {
7337                    orders.push(ordered);
7338                } else {
7339                    break;
7340                }
7341                if !self.match_token(TokenType::Comma) {
7342                    break;
7343                }
7344            }
7345            (
7346                Some(SortBy {
7347                    expressions: orders,
7348                }),
7349                None,
7350            )
7351        } else if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
7352            // CLUSTER BY
7353            let mut orders = Vec::new();
7354            loop {
7355                if let Some(ordered) = self.parse_ordered_item()? {
7356                    orders.push(ordered);
7357                } else {
7358                    break;
7359                }
7360                if !self.match_token(TokenType::Comma) {
7361                    break;
7362                }
7363            }
7364            (
7365                None,
7366                Some(ClusterBy {
7367                    expressions: orders,
7368                }),
7369            )
7370        } else {
7371            (None, None)
7372        };
7373
7374        // Parse ORDER BY
7375        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
7376            Some(self.parse_order_by()?)
7377        } else {
7378            None
7379        };
7380
7381        // Parse LIMIT
7382        let limit = if self.match_token(TokenType::Limit) {
7383            Some(Limit {
7384                this: self.parse_expression()?,
7385                percent: false,
7386                comments: Vec::new(),
7387            })
7388        } else {
7389            None
7390        };
7391
7392        // Parse OFFSET
7393        let offset = if self.match_token(TokenType::Offset) {
7394            Some(Offset {
7395                this: self.parse_expression()?,
7396                rows: None,
7397            })
7398        } else {
7399            None
7400        };
7401
7402        // If we have any modifiers, wrap in a Subquery with the modifiers
7403        if order_by.is_some()
7404            || limit.is_some()
7405            || offset.is_some()
7406            || distribute_by.is_some()
7407            || sort_by.is_some()
7408            || cluster_by.is_some()
7409        {
7410            // If inner is already a Subquery, add modifiers to it instead of double-wrapping
7411            if let Expression::Subquery(mut subq) = inner {
7412                subq.order_by = order_by;
7413                subq.limit = limit;
7414                subq.offset = offset;
7415                subq.distribute_by = distribute_by;
7416                subq.sort_by = sort_by;
7417                subq.cluster_by = cluster_by;
7418                Ok(Expression::Subquery(subq))
7419            } else if let Expression::Paren(paren) = inner {
7420                // If inner is a Paren containing a Subquery or other query, unwrap it
7421                // and add modifiers to a new Subquery wrapping the Paren
7422                // This handles cases like ((SELECT 1)) LIMIT 1
7423                Ok(Expression::Subquery(Box::new(Subquery {
7424                    this: Expression::Paren(paren),
7425                    alias: None,
7426                    column_aliases: Vec::new(),
7427                    order_by,
7428                    limit,
7429                    offset,
7430                    distribute_by,
7431                    sort_by,
7432                    cluster_by,
7433                    lateral: false,
7434                    modifiers_inside: false,
7435                    trailing_comments: Vec::new(),
7436                    inferred_type: None,
7437                })))
7438            } else {
7439                Ok(Expression::Subquery(Box::new(Subquery {
7440                    this: inner,
7441                    alias: None,
7442                    column_aliases: Vec::new(),
7443                    order_by,
7444                    limit,
7445                    offset,
7446                    distribute_by,
7447                    sort_by,
7448                    cluster_by,
7449                    lateral: false,
7450                    modifiers_inside: false,
7451                    trailing_comments: Vec::new(),
7452                    inferred_type: None,
7453                })))
7454            }
7455        } else {
7456            // No modifiers - return inner as-is (don't double-wrap if already a Subquery)
7457            Ok(inner)
7458        }
7459    }
7460
7461    /// Parse ORDER BY expressions for use inside aggregate functions
7462    /// Returns Vec<Ordered> instead of OrderBy struct
7463    fn parse_order_by_list(&mut self) -> Result<Vec<Ordered>> {
7464        let mut expressions = Vec::new();
7465
7466        loop {
7467            let expr = self.parse_expression()?;
7468
7469            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7470                (true, false)
7471            } else if self.match_token(TokenType::Asc) {
7472                (false, true)
7473            } else {
7474                (false, false)
7475            };
7476
7477            let nulls_first = if self.match_token(TokenType::Nulls) {
7478                if self.match_token(TokenType::First) {
7479                    Some(true)
7480                } else if self.match_token(TokenType::Last) {
7481                    Some(false)
7482                } else {
7483                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7484                }
7485            } else {
7486                None
7487            };
7488
7489            expressions.push(Ordered {
7490                this: expr,
7491                desc,
7492                nulls_first,
7493                explicit_asc,
7494                with_fill: None,
7495            });
7496
7497            if !self.match_token(TokenType::Comma) {
7498                break;
7499            }
7500        }
7501
7502        Ok(expressions)
7503    }
7504
7505    /// Parse DISTRIBUTE BY clause (Hive/Spark)
7506    fn parse_distribute_by(&mut self) -> Result<DistributeBy> {
7507        let mut expressions = Vec::new();
7508
7509        loop {
7510            expressions.push(self.parse_expression()?);
7511            if !self.match_token(TokenType::Comma) {
7512                break;
7513            }
7514        }
7515
7516        Ok(DistributeBy { expressions })
7517    }
7518
7519    /// Parse CLUSTER BY clause (Hive/Spark)
7520    fn parse_cluster_by(&mut self) -> Result<ClusterBy> {
7521        let mut expressions = Vec::new();
7522
7523        loop {
7524            let expr = self.parse_expression()?;
7525
7526            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7527                (true, false)
7528            } else if self.match_token(TokenType::Asc) {
7529                (false, true)
7530            } else {
7531                (false, false)
7532            };
7533
7534            expressions.push(Ordered {
7535                this: expr,
7536                desc,
7537                nulls_first: None,
7538                explicit_asc,
7539                with_fill: None,
7540            });
7541
7542            if !self.match_token(TokenType::Comma) {
7543                break;
7544            }
7545        }
7546
7547        Ok(ClusterBy { expressions })
7548    }
7549
7550    /// Parse SORT BY clause (Hive/Spark)
7551    fn parse_sort_by(&mut self) -> Result<SortBy> {
7552        let mut expressions = Vec::new();
7553
7554        loop {
7555            let expr = self.parse_expression()?;
7556
7557            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7558                (true, false)
7559            } else if self.match_token(TokenType::Asc) {
7560                (false, true)
7561            } else {
7562                (false, false)
7563            };
7564
7565            let nulls_first = if self.match_token(TokenType::Nulls) {
7566                if self.match_token(TokenType::First) {
7567                    Some(true)
7568                } else if self.match_token(TokenType::Last) {
7569                    Some(false)
7570                } else {
7571                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7572                }
7573            } else {
7574                None
7575            };
7576
7577            expressions.push(Ordered {
7578                this: expr,
7579                desc,
7580                nulls_first,
7581                explicit_asc,
7582                with_fill: None,
7583            });
7584
7585            if !self.match_token(TokenType::Comma) {
7586                break;
7587            }
7588        }
7589
7590        Ok(SortBy { expressions })
7591    }
7592
7593    /// Parse FOR UPDATE/SHARE locking clauses or FOR XML (T-SQL)
7594    /// Syntax: FOR UPDATE|SHARE|NO KEY UPDATE|KEY SHARE [OF tables] [NOWAIT|WAIT n|SKIP LOCKED]
7595    /// Also handles: LOCK IN SHARE MODE (MySQL)
7596    /// Also handles: FOR XML PATH|RAW|AUTO|EXPLICIT [, options...] (T-SQL)
7597    fn parse_locks_and_for_xml(&mut self) -> Result<(Vec<Lock>, Vec<Expression>)> {
7598        let mut locks = Vec::new();
7599        let mut for_xml = Vec::new();
7600
7601        loop {
7602            let (update, key) = if self.match_keywords(&[TokenType::For, TokenType::Update]) {
7603                // FOR UPDATE
7604                (
7605                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7606                        value: true,
7607                    }))),
7608                    None,
7609                )
7610            } else if self.check(TokenType::For) && self.check_next_identifier("XML") {
7611                // FOR XML (T-SQL) - parse XML options
7612                self.skip(); // consume FOR
7613                self.skip(); // consume XML
7614                for_xml = self.parse_for_xml_options()?;
7615                break; // FOR XML is always the last clause
7616            } else if self.check(TokenType::For) && self.check_next_identifier("SHARE") {
7617                // FOR SHARE
7618                self.skip(); // consume FOR
7619                self.skip(); // consume SHARE
7620                (None, None)
7621            } else if self.check_identifier("LOCK") && self.check_next(TokenType::In) {
7622                // LOCK IN SHARE MODE (MySQL) -> converted to FOR SHARE
7623                self.skip(); // consume LOCK
7624                self.skip(); // consume IN
7625                if self.match_identifier("SHARE") {
7626                    let _ = self.match_identifier("MODE");
7627                }
7628                (None, None)
7629            } else if self.check(TokenType::For) && self.check_next(TokenType::Key) {
7630                // FOR KEY SHARE (PostgreSQL)
7631                self.skip(); // consume FOR
7632                self.skip(); // consume KEY
7633                if !self.match_identifier("SHARE") {
7634                    break; // Not a valid lock clause
7635                }
7636                (
7637                    None,
7638                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7639                        value: true,
7640                    }))),
7641                )
7642            } else if self.check(TokenType::For) && self.check_next(TokenType::No) {
7643                // FOR NO KEY UPDATE (PostgreSQL)
7644                self.skip(); // consume FOR
7645                self.skip(); // consume NO
7646                if !self.match_identifier("KEY") || !self.match_token(TokenType::Update) {
7647                    break; // Not a valid lock clause
7648                }
7649                (
7650                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7651                        value: true,
7652                    }))),
7653                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7654                        value: true,
7655                    }))),
7656                )
7657            } else {
7658                // No more lock clauses
7659                break;
7660            };
7661
7662            // Parse optional OF clause: OF table1, table2
7663            let expressions = if self.match_token(TokenType::Of) {
7664                let mut tables = Vec::new();
7665                loop {
7666                    // Parse table reference (can be schema.table or just table)
7667                    let table = self.parse_table_ref()?;
7668                    tables.push(Expression::Table(Box::new(table)));
7669                    if !self.match_token(TokenType::Comma) {
7670                        break;
7671                    }
7672                }
7673                tables
7674            } else {
7675                Vec::new()
7676            };
7677
7678            // Parse wait option: NOWAIT, WAIT n, or SKIP LOCKED
7679            // Following Python sqlglot convention:
7680            // - NOWAIT -> Boolean(true)
7681            // - SKIP LOCKED -> Boolean(false)
7682            // - WAIT n -> Literal (the number)
7683            let wait = if self.match_identifier("NOWAIT") {
7684                // NOWAIT -> represented as Boolean(true)
7685                Some(Box::new(Expression::Boolean(BooleanLiteral {
7686                    value: true,
7687                })))
7688            } else if self.match_identifier("WAIT") {
7689                // WAIT n -> wait = expression (the number/literal)
7690                Some(Box::new(self.parse_primary()?))
7691            } else if self.match_identifier("SKIP") && self.match_identifier("LOCKED") {
7692                // SKIP LOCKED -> represented as Boolean(false)
7693                Some(Box::new(Expression::Boolean(BooleanLiteral {
7694                    value: false,
7695                })))
7696            } else {
7697                None
7698            };
7699
7700            locks.push(Lock {
7701                update,
7702                expressions,
7703                wait,
7704                key,
7705            });
7706        }
7707
7708        Ok((locks, for_xml))
7709    }
7710
7711    /// Parse FOR XML options (T-SQL)
7712    /// Syntax: FOR XML PATH|RAW|AUTO|EXPLICIT [('element')] [, BINARY BASE64] [, ELEMENTS [XSINIL|ABSENT]] [, TYPE] [, ROOT('name')]
7713    fn parse_for_xml_options(&mut self) -> Result<Vec<Expression>> {
7714        let mut options = Vec::new();
7715
7716        loop {
7717            // Parse XML option: could be a known option (PATH, RAW, AUTO, EXPLICIT, BINARY, ELEMENTS, TYPE, ROOT)
7718            // or an XMLKeyValueOption like PATH('element')
7719            if let Some(opt) = self.parse_for_xml_single_option()? {
7720                options.push(opt);
7721            } else {
7722                break;
7723            }
7724
7725            // Check for comma to continue parsing more options
7726            if !self.match_token(TokenType::Comma) {
7727                break;
7728            }
7729        }
7730
7731        Ok(options)
7732    }
7733
7734    /// Parse a single FOR XML option
7735    fn parse_for_xml_single_option(&mut self) -> Result<Option<Expression>> {
7736        // Known XML modes: PATH, RAW, AUTO, EXPLICIT
7737        // Known options: BINARY BASE64, ELEMENTS [XSINIL|ABSENT], TYPE, ROOT('name')
7738
7739        // Try to match known patterns
7740        if self.match_identifier("PATH") {
7741            let expression = if self.match_token(TokenType::LParen) {
7742                let expr = self.parse_string()?;
7743                self.expect(TokenType::RParen)?;
7744                expr
7745            } else {
7746                None
7747            };
7748            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7749                this: Box::new(Expression::Var(Box::new(Var {
7750                    this: "PATH".to_string(),
7751                }))),
7752                expression: expression.map(|e| Box::new(e)),
7753            }))));
7754        }
7755
7756        if self.match_identifier("RAW") {
7757            let expression = if self.match_token(TokenType::LParen) {
7758                let expr = self.parse_string()?;
7759                self.expect(TokenType::RParen)?;
7760                expr
7761            } else {
7762                None
7763            };
7764            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7765                this: Box::new(Expression::Var(Box::new(Var {
7766                    this: "RAW".to_string(),
7767                }))),
7768                expression: expression.map(|e| Box::new(e)),
7769            }))));
7770        }
7771
7772        if self.match_identifier("AUTO") {
7773            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7774                this: Box::new(Expression::Var(Box::new(Var {
7775                    this: "AUTO".to_string(),
7776                }))),
7777                expression: None,
7778            }))));
7779        }
7780
7781        if self.match_identifier("EXPLICIT") {
7782            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7783                this: Box::new(Expression::Var(Box::new(Var {
7784                    this: "EXPLICIT".to_string(),
7785                }))),
7786                expression: None,
7787            }))));
7788        }
7789
7790        if self.match_identifier("TYPE") {
7791            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7792                this: Box::new(Expression::Var(Box::new(Var {
7793                    this: "TYPE".to_string(),
7794                }))),
7795                expression: None,
7796            }))));
7797        }
7798
7799        if self.match_identifier("BINARY") {
7800            // BINARY BASE64
7801            if self.match_identifier("BASE64") {
7802                return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7803                    this: Box::new(Expression::Var(Box::new(Var {
7804                        this: "BINARY BASE64".to_string(),
7805                    }))),
7806                    expression: None,
7807                }))));
7808            } else {
7809                return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7810                    this: Box::new(Expression::Var(Box::new(Var {
7811                        this: "BINARY".to_string(),
7812                    }))),
7813                    expression: None,
7814                }))));
7815            }
7816        }
7817
7818        if self.match_identifier("ELEMENTS") {
7819            // ELEMENTS [XSINIL|ABSENT]
7820            let suboption = if self.match_identifier("XSINIL") {
7821                Some("XSINIL".to_string())
7822            } else if self.match_identifier("ABSENT") {
7823                Some("ABSENT".to_string())
7824            } else {
7825                None
7826            };
7827            let option_name = match &suboption {
7828                Some(sub) => format!("ELEMENTS {}", sub),
7829                None => "ELEMENTS".to_string(),
7830            };
7831            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7832                this: Box::new(Expression::Var(Box::new(Var { this: option_name }))),
7833                expression: None,
7834            }))));
7835        }
7836
7837        if self.match_identifier("ROOT") {
7838            let expression = if self.match_token(TokenType::LParen) {
7839                let expr = self.parse_string()?;
7840                self.expect(TokenType::RParen)?;
7841                expr
7842            } else {
7843                None
7844            };
7845            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7846                this: Box::new(Expression::Var(Box::new(Var {
7847                    this: "ROOT".to_string(),
7848                }))),
7849                expression: expression.map(|e| Box::new(e)),
7850            }))));
7851        }
7852
7853        // No more options recognized
7854        Ok(None)
7855    }
7856
7857    /// Parse CONNECT BY clause (Oracle hierarchical queries)
7858    /// Syntax: [START WITH condition] CONNECT BY [NOCYCLE] condition [START WITH condition]
7859    /// START WITH can appear before or after CONNECT BY
7860    fn parse_connect(&mut self) -> Result<Option<Connect>> {
7861        // Check for START WITH first (can appear before CONNECT BY)
7862        let start_before = if self.match_keywords(&[TokenType::Start, TokenType::With]) {
7863            Some(self.parse_expression()?)
7864        } else {
7865            None
7866        };
7867
7868        // Check for CONNECT BY
7869        if !self.match_keywords(&[TokenType::Connect, TokenType::By]) {
7870            if start_before.is_some() {
7871                return Err(self.parse_error("START WITH without CONNECT BY"));
7872            }
7873            return Ok(None);
7874        }
7875
7876        // Check for NOCYCLE
7877        let nocycle = self.match_token(TokenType::NoCycle);
7878
7879        // Parse the CONNECT BY condition with PRIOR support
7880        let connect = self.parse_connect_expression()?;
7881
7882        // START WITH can also appear after CONNECT BY
7883        let start = if start_before.is_some() {
7884            start_before
7885        } else if self.match_keywords(&[TokenType::Start, TokenType::With]) {
7886            Some(self.parse_expression()?)
7887        } else {
7888            None
7889        };
7890
7891        Ok(Some(Connect {
7892            start,
7893            connect,
7894            nocycle,
7895        }))
7896    }
7897
7898    /// Parse expression in CONNECT BY context, treating PRIOR as prefix operator
7899    fn parse_connect_expression(&mut self) -> Result<Expression> {
7900        self.parse_connect_or()
7901    }
7902
7903    /// Parse OR expression in CONNECT BY context
7904    fn parse_connect_or(&mut self) -> Result<Expression> {
7905        let mut left = self.parse_connect_and()?;
7906
7907        while self.match_token(TokenType::Or) {
7908            let right = self.parse_connect_and()?;
7909            left = Expression::Or(Box::new(BinaryOp::new(left, right)));
7910        }
7911
7912        Ok(Self::maybe_rebalance_boolean_chain(left, false))
7913    }
7914
7915    /// Parse AND expression in CONNECT BY context
7916    fn parse_connect_and(&mut self) -> Result<Expression> {
7917        let mut left = self.parse_connect_comparison()?;
7918
7919        while self.match_token(TokenType::And) {
7920            let right = self.parse_connect_comparison()?;
7921            left = Expression::And(Box::new(BinaryOp::new(left, right)));
7922        }
7923
7924        Ok(Self::maybe_rebalance_boolean_chain(left, true))
7925    }
7926
7927    /// Parse comparison in CONNECT BY context
7928    fn parse_connect_comparison(&mut self) -> Result<Expression> {
7929        let left = self.parse_connect_primary()?;
7930
7931        if self.match_token(TokenType::Eq) {
7932            let right = self.parse_connect_primary()?;
7933            return Ok(Expression::Eq(Box::new(BinaryOp::new(left, right))));
7934        }
7935        if self.match_token(TokenType::Neq) {
7936            let right = self.parse_connect_primary()?;
7937            return Ok(Expression::Neq(Box::new(BinaryOp::new(left, right))));
7938        }
7939        if self.match_token(TokenType::Lt) {
7940            let right = self.parse_connect_primary()?;
7941            return Ok(Expression::Lt(Box::new(BinaryOp::new(left, right))));
7942        }
7943        if self.match_token(TokenType::Lte) {
7944            let right = self.parse_connect_primary()?;
7945            return Ok(Expression::Lte(Box::new(BinaryOp::new(left, right))));
7946        }
7947        if self.match_token(TokenType::Gt) {
7948            let right = self.parse_connect_primary()?;
7949            return Ok(Expression::Gt(Box::new(BinaryOp::new(left, right))));
7950        }
7951        if self.match_token(TokenType::Gte) {
7952            let right = self.parse_connect_primary()?;
7953            return Ok(Expression::Gte(Box::new(BinaryOp::new(left, right))));
7954        }
7955
7956        Ok(left)
7957    }
7958
7959    /// Parse primary in CONNECT BY context with PRIOR support
7960    fn parse_connect_primary(&mut self) -> Result<Expression> {
7961        // Handle PRIOR as prefix operator
7962        if self.match_token(TokenType::Prior) {
7963            let expr = self.parse_primary()?;
7964            return Ok(Expression::Prior(Box::new(Prior { this: expr })));
7965        }
7966
7967        if let Some(connect_by_root) = self.try_parse_connect_by_root_expression()? {
7968            return Ok(connect_by_root);
7969        }
7970
7971        self.parse_primary()
7972    }
7973
7974    /// Parse Oracle CONNECT_BY_ROOT in either supported form:
7975    /// CONNECT_BY_ROOT col
7976    /// CONNECT_BY_ROOT(col)
7977    fn try_parse_connect_by_root_expression(&mut self) -> Result<Option<Expression>> {
7978        if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("CONNECT_BY_ROOT"))
7979        {
7980            return Ok(None);
7981        }
7982
7983        self.skip();
7984
7985        let this = if self.match_token(TokenType::LParen) {
7986            let expr = self.parse_expression()?;
7987            self.expect(TokenType::RParen)?;
7988            expr
7989        } else {
7990            self.parse_column()?.ok_or_else(|| {
7991                self.parse_error("Expected expression or column after CONNECT_BY_ROOT")
7992            })?
7993        };
7994
7995        Ok(Some(Expression::ConnectByRoot(Box::new(ConnectByRoot {
7996            this,
7997        }))))
7998    }
7999
8000    /// Parse MATCH_RECOGNIZE clause (Oracle/Snowflake/Presto/Trino pattern matching)
8001    /// MATCH_RECOGNIZE ( [PARTITION BY ...] [ORDER BY ...] [MEASURES ...] [rows] [after] PATTERN (...) DEFINE ... )
8002    fn parse_match_recognize(&mut self, source: Option<Expression>) -> Result<Expression> {
8003        self.expect(TokenType::LParen)?;
8004
8005        // PARTITION BY (optional)
8006        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
8007            Some(self.parse_expression_list()?)
8008        } else {
8009            None
8010        };
8011
8012        // ORDER BY (optional)
8013        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
8014            Some(self.parse_order_by()?.expressions)
8015        } else {
8016            None
8017        };
8018
8019        // MEASURES (optional)
8020        let measures = if self.match_token(TokenType::Measures) {
8021            Some(self.parse_match_recognize_measures()?)
8022        } else {
8023            None
8024        };
8025
8026        // Row semantics: ONE ROW PER MATCH / ALL ROWS PER MATCH
8027        let rows = self.parse_match_recognize_rows()?;
8028
8029        // AFTER MATCH SKIP
8030        let after = self.parse_match_recognize_after()?;
8031
8032        // PATTERN
8033        let pattern = if self.match_token(TokenType::Pattern) {
8034            Some(self.parse_match_recognize_pattern()?)
8035        } else {
8036            None
8037        };
8038
8039        // DEFINE
8040        let define = if self.match_token(TokenType::Define) {
8041            Some(self.parse_match_recognize_define()?)
8042        } else {
8043            None
8044        };
8045
8046        self.expect(TokenType::RParen)?;
8047
8048        // Alias is handled by the caller
8049
8050        Ok(Expression::MatchRecognize(Box::new(MatchRecognize {
8051            this: source.map(Box::new),
8052            partition_by,
8053            order_by,
8054            measures,
8055            rows,
8056            after,
8057            pattern,
8058            define,
8059            alias: None,
8060            alias_explicit_as: false,
8061        })))
8062    }
8063
8064    /// Parse MEASURES clause in MATCH_RECOGNIZE
8065    fn parse_match_recognize_measures(&mut self) -> Result<Vec<MatchRecognizeMeasure>> {
8066        let mut measures = Vec::new();
8067
8068        loop {
8069            // Check for RUNNING or FINAL
8070            let window_frame = if self.match_token(TokenType::Running) {
8071                Some(MatchRecognizeSemantics::Running)
8072            } else if self.match_token(TokenType::Final) {
8073                Some(MatchRecognizeSemantics::Final)
8074            } else {
8075                None
8076            };
8077
8078            let mut expr = self.parse_expression()?;
8079
8080            // Handle AS alias for measures
8081            if self.match_token(TokenType::As) {
8082                let alias = Identifier::new(self.expect_identifier()?);
8083                expr = Expression::Alias(Box::new(Alias::new(expr, alias)));
8084            }
8085
8086            measures.push(MatchRecognizeMeasure {
8087                this: expr,
8088                window_frame,
8089            });
8090
8091            if !self.match_token(TokenType::Comma) {
8092                break;
8093            }
8094        }
8095
8096        Ok(measures)
8097    }
8098
8099    /// Parse row semantics in MATCH_RECOGNIZE
8100    fn parse_match_recognize_rows(&mut self) -> Result<Option<MatchRecognizeRows>> {
8101        // ONE ROW PER MATCH
8102        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ONE") {
8103            self.skip(); // consume ONE
8104            if !self.match_token(TokenType::Row) {
8105                return Err(self.parse_error("Expected ROW after ONE"));
8106            }
8107            if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PER")) {
8108                return Err(self.parse_error("Expected PER after ONE ROW"));
8109            }
8110            self.skip(); // consume PER
8111            if !self.match_token(TokenType::Match) {
8112                return Err(self.parse_error("Expected MATCH after ONE ROW PER"));
8113            }
8114            return Ok(Some(MatchRecognizeRows::OneRowPerMatch));
8115        }
8116
8117        // ALL ROWS PER MATCH [variants]
8118        if self.match_token(TokenType::All) {
8119            if !self.match_token(TokenType::Rows) {
8120                return Err(self.parse_error("Expected ROWS after ALL"));
8121            }
8122            if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PER")) {
8123                return Err(self.parse_error("Expected PER after ALL ROWS"));
8124            }
8125            self.skip(); // consume PER
8126            if !self.match_token(TokenType::Match) {
8127                return Err(self.parse_error("Expected MATCH after ALL ROWS PER"));
8128            }
8129
8130            // Check for optional modifiers
8131            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SHOW") {
8132                self.skip();
8133                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EMPTY") {
8134                    self.skip();
8135                    if self.check(TokenType::Var)
8136                        && self.peek().text.eq_ignore_ascii_case("MATCHES")
8137                    {
8138                        self.skip();
8139                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchShowEmptyMatches));
8140                    }
8141                }
8142                return Err(self.parse_error("Expected EMPTY MATCHES after SHOW"));
8143            }
8144
8145            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OMIT") {
8146                self.skip();
8147                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EMPTY") {
8148                    self.skip();
8149                    if self.check(TokenType::Var)
8150                        && self.peek().text.eq_ignore_ascii_case("MATCHES")
8151                    {
8152                        self.skip();
8153                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchOmitEmptyMatches));
8154                    }
8155                }
8156                return Err(self.parse_error("Expected EMPTY MATCHES after OMIT"));
8157            }
8158
8159            if self.match_token(TokenType::With) {
8160                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("UNMATCHED")
8161                {
8162                    self.skip();
8163                    if self.match_token(TokenType::Rows) {
8164                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchWithUnmatchedRows));
8165                    }
8166                }
8167                return Err(self.parse_error("Expected UNMATCHED ROWS after WITH"));
8168            }
8169
8170            return Ok(Some(MatchRecognizeRows::AllRowsPerMatch));
8171        }
8172
8173        Ok(None)
8174    }
8175
8176    /// Parse AFTER MATCH SKIP clause in MATCH_RECOGNIZE
8177    fn parse_match_recognize_after(&mut self) -> Result<Option<MatchRecognizeAfter>> {
8178        if !self.match_token(TokenType::After) {
8179            return Ok(None);
8180        }
8181
8182        if !self.match_token(TokenType::Match) {
8183            return Err(self.parse_error("Expected MATCH after AFTER"));
8184        }
8185
8186        // Check for SKIP (it might be an identifier)
8187        if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SKIP")) {
8188            return Err(self.parse_error("Expected SKIP after AFTER MATCH"));
8189        }
8190        self.skip(); // consume SKIP
8191
8192        // PAST LAST ROW
8193        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PAST") {
8194            self.skip();
8195            if self.match_token(TokenType::Last) {
8196                if self.match_token(TokenType::Row) {
8197                    return Ok(Some(MatchRecognizeAfter::PastLastRow));
8198                }
8199            }
8200            return Err(self.parse_error("Expected LAST ROW after PAST"));
8201        }
8202
8203        // TO NEXT ROW / TO FIRST x / TO LAST x
8204        if self.match_token(TokenType::To) {
8205            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("NEXT") {
8206                self.skip();
8207                if self.match_token(TokenType::Row) {
8208                    return Ok(Some(MatchRecognizeAfter::ToNextRow));
8209                }
8210                return Err(self.parse_error("Expected ROW after NEXT"));
8211            }
8212
8213            if self.match_token(TokenType::First) {
8214                let name = self.expect_identifier()?;
8215                return Ok(Some(MatchRecognizeAfter::ToFirst(Identifier::new(name))));
8216            }
8217
8218            if self.match_token(TokenType::Last) {
8219                let name = self.expect_identifier()?;
8220                return Ok(Some(MatchRecognizeAfter::ToLast(Identifier::new(name))));
8221            }
8222
8223            return Err(self.parse_error("Expected NEXT ROW, FIRST x, or LAST x after TO"));
8224        }
8225
8226        Err(self.parse_error("Expected PAST LAST ROW or TO ... after AFTER MATCH SKIP"))
8227    }
8228
8229    /// Parse PATTERN clause in MATCH_RECOGNIZE using bracket counting
8230    fn parse_match_recognize_pattern(&mut self) -> Result<String> {
8231        self.expect(TokenType::LParen)?;
8232
8233        let mut depth = 1;
8234        let mut pattern = String::new();
8235
8236        while depth > 0 && !self.is_at_end() {
8237            let token = self.advance();
8238            match token.token_type {
8239                TokenType::LParen => {
8240                    depth += 1;
8241                    pattern.push('(');
8242                }
8243                TokenType::RParen => {
8244                    depth -= 1;
8245                    if depth > 0 {
8246                        pattern.push(')');
8247                    }
8248                }
8249                _ => {
8250                    // Pattern quantifiers (+, *, ?, {n,m}) should not have a space before them
8251                    let is_quantifier = matches!(token.text.as_str(), "+" | "*" | "?")
8252                        || token.text.starts_with('{');
8253
8254                    if !pattern.is_empty()
8255                        && !pattern.ends_with('(')
8256                        && !pattern.ends_with(' ')
8257                        && !is_quantifier
8258                    {
8259                        pattern.push(' ');
8260                    }
8261                    pattern.push_str(&token.text);
8262                }
8263            }
8264        }
8265
8266        if depth > 0 {
8267            return Err(self.parse_error("Unclosed parenthesis in PATTERN clause"));
8268        }
8269
8270        Ok(pattern.trim().to_string())
8271    }
8272
8273    /// Parse DEFINE clause in MATCH_RECOGNIZE
8274    fn parse_match_recognize_define(&mut self) -> Result<Vec<(Identifier, Expression)>> {
8275        let mut definitions = Vec::new();
8276
8277        loop {
8278            let name = Identifier::new(self.expect_identifier()?);
8279            self.expect(TokenType::As)?;
8280            let expr = self.parse_expression()?;
8281
8282            definitions.push((name, expr));
8283
8284            if !self.match_token(TokenType::Comma) {
8285                break;
8286            }
8287        }
8288
8289        Ok(definitions)
8290    }
8291
8292    /// Parse LATERAL VIEW clauses (Hive/Spark)
8293    /// Syntax: LATERAL VIEW [OUTER] generator_function(args) table_alias AS col1 [, col2, ...]
8294    fn parse_lateral_views(&mut self) -> Result<Vec<LateralView>> {
8295        let mut views = Vec::new();
8296
8297        while self.match_keywords(&[TokenType::Lateral, TokenType::View]) {
8298            // Check for OUTER keyword
8299            let outer = self.match_token(TokenType::Outer);
8300
8301            // Parse the generator function (EXPLODE, POSEXPLODE, INLINE, etc.)
8302            // This is a function call expression
8303            let this = self.parse_primary()?;
8304
8305            // Parse table alias (comes before AS)
8306            let table_alias = if self.check(TokenType::Var) && !self.check_keyword() {
8307                Some(Identifier::new(self.expect_identifier()?))
8308            } else {
8309                None
8310            };
8311
8312            // Parse column aliases after AS keyword
8313            // Supports both: AS a, b and AS (a, b)
8314            let column_aliases = if self.match_token(TokenType::As) {
8315                let mut aliases = Vec::new();
8316                // Check for parenthesized alias list: AS ("a", "b")
8317                if self.match_token(TokenType::LParen) {
8318                    loop {
8319                        aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
8320                        if !self.match_token(TokenType::Comma) {
8321                            break;
8322                        }
8323                    }
8324                    self.expect(TokenType::RParen)?;
8325                } else {
8326                    // Non-parenthesized aliases: AS a, b, c
8327                    // Use expect_identifier_or_keyword because aliases like "key", "value", "pos" may be keywords
8328                    loop {
8329                        aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
8330                        if !self.match_token(TokenType::Comma) {
8331                            break;
8332                        }
8333                        // Check if next token is still an identifier or keyword (column alias)
8334                        // vs starting a new LATERAL VIEW or other clause
8335                        if !self.is_identifier_or_keyword_token() {
8336                            break;
8337                        }
8338                        // Check for keywords that would end the column list
8339                        if self.peek().token_type == TokenType::Lateral
8340                            || self.peek().token_type == TokenType::Where
8341                            || self.peek().token_type == TokenType::Group
8342                            || self.peek().token_type == TokenType::Having
8343                            || self.peek().token_type == TokenType::Order
8344                            || self.peek().token_type == TokenType::Limit
8345                        {
8346                            break;
8347                        }
8348                    }
8349                }
8350                aliases
8351            } else {
8352                Vec::new()
8353            };
8354
8355            views.push(LateralView {
8356                this,
8357                table_alias,
8358                column_aliases,
8359                outer,
8360            });
8361        }
8362
8363        Ok(views)
8364    }
8365
8366    /// Parse named windows (WINDOW w AS (...), ...)
8367    fn parse_named_windows(&mut self) -> Result<Vec<NamedWindow>> {
8368        let mut windows = Vec::new();
8369
8370        loop {
8371            let name = self.expect_identifier()?;
8372            self.expect(TokenType::As)?;
8373            self.expect(TokenType::LParen)?;
8374
8375            // Parse optional base window name reference (e.g., w1 AS (w0 ORDER BY ...))
8376            let window_name = if (self.check(TokenType::Identifier)
8377                || self.check(TokenType::Var)
8378                || self.check(TokenType::QuotedIdentifier))
8379                && !self.check(TokenType::Partition)
8380                && !self.check(TokenType::Order)
8381                && self.peek_nth(1).map_or(true, |t| {
8382                    matches!(
8383                        t.token_type,
8384                        TokenType::Partition
8385                            | TokenType::Order
8386                            | TokenType::Rows
8387                            | TokenType::Range
8388                            | TokenType::Groups
8389                            | TokenType::RParen
8390                            | TokenType::Comma
8391                    )
8392                }) {
8393                Some(self.expect_identifier()?)
8394            } else {
8395                None
8396            };
8397
8398            // Parse window specification
8399            let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
8400                Some(self.parse_expression_list()?)
8401            } else {
8402                None
8403            };
8404
8405            let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
8406                Some(self.parse_order_by()?)
8407            } else {
8408                None
8409            };
8410
8411            let frame = self.parse_window_frame()?;
8412
8413            self.expect(TokenType::RParen)?;
8414
8415            windows.push(NamedWindow {
8416                name: Identifier::new(name),
8417                spec: Over {
8418                    window_name: window_name.map(|n| Identifier::new(n)),
8419                    partition_by: partition_by.unwrap_or_default(),
8420                    order_by: order_by.map(|o| o.expressions).unwrap_or_default(),
8421                    frame,
8422                    alias: None,
8423                },
8424            });
8425
8426            if !self.match_token(TokenType::Comma) {
8427                break;
8428            }
8429        }
8430
8431        Ok(windows)
8432    }
8433
8434    /// Parse query hint /*+ ... */
8435    fn parse_hint(&mut self) -> Result<Hint> {
8436        let token = self.advance();
8437        let hint_text = token.text.clone();
8438
8439        // For now, parse as raw hint text
8440        // More sophisticated parsing can be added later
8441        let expressions = if hint_text.is_empty() {
8442            Vec::new()
8443        } else {
8444            vec![HintExpression::Raw(hint_text)]
8445        };
8446
8447        Ok(Hint { expressions })
8448    }
8449
8450    /// Parse SAMPLE / TABLESAMPLE / USING SAMPLE clause
8451    fn parse_sample_clause(&mut self) -> Result<Option<Sample>> {
8452        // Check for USING SAMPLE (DuckDB), SAMPLE, or TABLESAMPLE
8453        let is_using_sample = if self.check(TokenType::Using)
8454            && self.current + 1 < self.tokens.len()
8455            && self.tokens[self.current + 1].token_type == TokenType::Sample
8456        {
8457            self.skip(); // consume USING
8458            self.skip(); // consume SAMPLE
8459            true
8460        } else {
8461            false
8462        };
8463
8464        let use_sample_keyword = if is_using_sample {
8465            // USING SAMPLE acts like SAMPLE
8466            true
8467        } else if self.match_token(TokenType::Sample) {
8468            true
8469        } else if self.match_token(TokenType::TableSample) {
8470            false
8471        } else {
8472            return Ok(None);
8473        };
8474
8475        // Parse sampling method if specified (BERNOULLI, SYSTEM, BLOCK, ROW, RESERVOIR)
8476        let (method, method_before_size, explicit_method) =
8477            if self.match_token(TokenType::Bernoulli) {
8478                (SampleMethod::Bernoulli, true, true)
8479            } else if self.match_token(TokenType::System) {
8480                (SampleMethod::System, true, true)
8481            } else if self.match_token(TokenType::Block) {
8482                (SampleMethod::Block, true, true)
8483            } else if self.match_token(TokenType::Row) {
8484                (SampleMethod::Row, true, true)
8485            } else if self.check_identifier("RESERVOIR") {
8486                self.skip();
8487                (SampleMethod::Reservoir, true, true)
8488            } else {
8489                // Default to BERNOULLI for both SAMPLE and TABLESAMPLE
8490                // This matches Python SQLGlot's normalization behavior
8491                (SampleMethod::Bernoulli, false, false)
8492            };
8493
8494        // Parse size (can be in parentheses)
8495        let has_paren = self.match_token(TokenType::LParen);
8496
8497        // Check for BUCKET syntax: TABLESAMPLE (BUCKET 1 OUT OF 5 ON x)
8498        if self.match_identifier("BUCKET") {
8499            let bucket_numerator = self.parse_primary()?;
8500            self.match_identifier("OUT");
8501            self.match_token(TokenType::Of); // OF is a keyword token
8502            let bucket_denominator = self.parse_primary()?;
8503            let bucket_field = if self.match_token(TokenType::On) {
8504                Some(Box::new(self.parse_primary()?))
8505            } else {
8506                None
8507            };
8508            if has_paren {
8509                self.expect(TokenType::RParen)?;
8510            }
8511            return Ok(Some(Sample {
8512                method: SampleMethod::Bucket,
8513                size: bucket_numerator.clone(),
8514                seed: None,
8515                offset: None,
8516                unit_after_size: false,
8517                use_sample_keyword,
8518                explicit_method: true,     // BUCKET is always explicit
8519                method_before_size: false, // BUCKET appears inside parens
8520                use_seed_keyword: false,
8521                bucket_numerator: Some(Box::new(bucket_numerator)),
8522                bucket_denominator: Some(Box::new(bucket_denominator)),
8523                bucket_field,
8524                is_using_sample,
8525                is_percent: false,
8526                suppress_method_output: false,
8527            }));
8528        }
8529
8530        // Use parse_unary to avoid consuming PERCENT as modulo operator
8531        let size = self.parse_unary()?;
8532
8533        // Check for PERCENT/ROWS suffix after size (if not already part of the number)
8534        // Both "%" and "PERCENT" tokens map to TokenType::Percent - accept both as PERCENT modifier
8535        let (method, unit_after_size, is_percent) = if self.check(TokenType::Percent) {
8536            self.skip(); // consume PERCENT or %
8537                         // If method was already explicitly specified (e.g., SYSTEM), keep it
8538                         // PERCENT here is just the unit, not the sampling method
8539            if method_before_size {
8540                (method, true, true)
8541            } else {
8542                (SampleMethod::Percent, true, true)
8543            }
8544        } else if self.match_token(TokenType::Rows) {
8545            // If method was already explicitly specified, keep it
8546            if method_before_size {
8547                (method, true, false)
8548            } else {
8549                (SampleMethod::Row, true, false)
8550            }
8551        } else {
8552            // No explicit unit after size - preserve the original method
8553            (method, false, false)
8554        };
8555
8556        if has_paren {
8557            self.expect(TokenType::RParen)?;
8558        }
8559
8560        // DuckDB USING SAMPLE: method and optional seed can come in parens after size
8561        // e.g., "10 PERCENT (bernoulli)" or "10% (system, 377)"
8562        // DuckDB USING SAMPLE: method and optional seed can come in parens after size
8563        // e.g., "10 PERCENT (bernoulli)" or "10% (system, 377)"
8564        let (method, seed, use_seed_keyword, explicit_method) =
8565            if is_using_sample && self.check(TokenType::LParen) {
8566                self.skip(); // consume LParen
8567                             // Parse method name as identifier or keyword token
8568                             // BERNOULLI, SYSTEM, RESERVOIR can be tokenized as keywords, not identifiers
8569                let method_from_parens =
8570                    if self.check_identifier("BERNOULLI") || self.check(TokenType::Bernoulli) {
8571                        self.skip();
8572                        Some(SampleMethod::Bernoulli)
8573                    } else if self.check_identifier("SYSTEM") || self.check(TokenType::System) {
8574                        self.skip();
8575                        Some(SampleMethod::System)
8576                    } else if self.check_identifier("RESERVOIR") {
8577                        self.skip();
8578                        Some(SampleMethod::Reservoir)
8579                    } else {
8580                        None
8581                    };
8582                // Optional seed after comma
8583                let seed = if self.match_token(TokenType::Comma) {
8584                    Some(self.parse_expression()?)
8585                } else {
8586                    None
8587                };
8588                self.expect(TokenType::RParen)?;
8589                let final_method = method_from_parens.unwrap_or(method);
8590                (final_method, seed, false, true)
8591            } else {
8592                // Parse optional SEED / REPEATABLE
8593                let (seed, use_seed_keyword) = if self.match_token(TokenType::Seed) {
8594                    self.expect(TokenType::LParen)?;
8595                    let seed_value = self.parse_expression()?;
8596                    self.expect(TokenType::RParen)?;
8597                    (Some(seed_value), true)
8598                } else if self.match_token(TokenType::Repeatable) {
8599                    self.expect(TokenType::LParen)?;
8600                    let seed_value = self.parse_expression()?;
8601                    self.expect(TokenType::RParen)?;
8602                    (Some(seed_value), false)
8603                } else {
8604                    (None, false)
8605                };
8606                let explicit_method = explicit_method || unit_after_size;
8607                (method, seed, use_seed_keyword, explicit_method)
8608            };
8609
8610        // For DuckDB USING SAMPLE: apply default methods
8611        // - bare number -> RESERVOIR, ROWS
8612        // - percent -> SYSTEM, PERCENT
8613        let (method, unit_after_size) = if is_using_sample && !explicit_method {
8614            // No explicit method - apply defaults
8615            (SampleMethod::Reservoir, false) // default: RESERVOIR with ROWS
8616        } else if is_using_sample && unit_after_size && !method_before_size {
8617            // Unit was specified after size (e.g., "10 PERCENT") but no method before
8618            // Check if method was set in post-parens
8619            if matches!(method, SampleMethod::Percent) {
8620                // "10%" or "10 PERCENT" without method -> SYSTEM
8621                (SampleMethod::System, true)
8622            } else if matches!(method, SampleMethod::Row) {
8623                // "50 ROWS" without method -> RESERVOIR
8624                (SampleMethod::Reservoir, true)
8625            } else {
8626                (method, unit_after_size)
8627            }
8628        } else {
8629            (method, unit_after_size)
8630        };
8631
8632        // method_before_size: true for USING SAMPLE - we normalize to method-before-size format
8633        // e.g., "10 PERCENT (bernoulli)" becomes "BERNOULLI (10 PERCENT)"
8634        Ok(Some(Sample {
8635            method,
8636            size,
8637            seed,
8638            offset: None,
8639            unit_after_size,
8640            use_sample_keyword,
8641            explicit_method: true,    // For USING SAMPLE, always explicit
8642            method_before_size: true, // Normalize to method-before-size format
8643            use_seed_keyword,
8644            bucket_numerator: None,
8645            bucket_denominator: None,
8646            bucket_field: None,
8647            is_using_sample,
8648            is_percent,
8649            suppress_method_output: false,
8650        }))
8651    }
8652
8653    /// Parse table-level TABLESAMPLE/SAMPLE: TABLESAMPLE/SAMPLE METHOD(size [PERCENT|ROWS])
8654    /// e.g., TABLESAMPLE RESERVOIR(20%), SAMPLE BERNOULLI(10 PERCENT), SAMPLE ROW(0)
8655    fn parse_table_level_sample(&mut self) -> Result<Option<Sample>> {
8656        // Accept both TABLESAMPLE and SAMPLE (Snowflake supports both)
8657        let use_sample_keyword = if self.match_token(TokenType::Sample) {
8658            true
8659        } else if self.match_token(TokenType::TableSample) {
8660            false
8661        } else {
8662            return Ok(None);
8663        };
8664        // Track which keyword was used for identity output
8665        let _ = use_sample_keyword; // Used below for is_using_sample field
8666
8667        // Teradata: SAMPLE 5 or SAMPLE 0.33, .25, .1 (no parentheses)
8668        if matches!(
8669            self.config.dialect,
8670            Some(crate::dialects::DialectType::Teradata)
8671        ) && use_sample_keyword
8672            && !self.check(TokenType::LParen)
8673        {
8674            let mut expressions = vec![self.parse_unary()?];
8675            while self.match_token(TokenType::Comma) {
8676                expressions.push(self.parse_unary()?);
8677            }
8678            let size = if expressions.len() == 1 {
8679                expressions.into_iter().next().unwrap()
8680            } else {
8681                Expression::Tuple(Box::new(Tuple { expressions }))
8682            };
8683            return Ok(Some(Sample {
8684                method: SampleMethod::Percent,
8685                size,
8686                seed: None,
8687                offset: None,
8688                unit_after_size: false,
8689                use_sample_keyword,
8690                explicit_method: false,
8691                method_before_size: false,
8692                use_seed_keyword: false,
8693                bucket_numerator: None,
8694                bucket_denominator: None,
8695                bucket_field: None,
8696                is_using_sample: false,
8697                is_percent: false,
8698                suppress_method_output: false,
8699            }));
8700        }
8701
8702        // ClickHouse: SAMPLE 0.1 [OFFSET 0.2] (no parentheses)
8703        if matches!(
8704            self.config.dialect,
8705            Some(crate::dialects::DialectType::ClickHouse)
8706        ) && use_sample_keyword
8707            && !self.check(TokenType::LParen)
8708        {
8709            let size = self.parse_expression()?;
8710            let offset = if self.match_token(TokenType::Offset) {
8711                Some(self.parse_expression()?)
8712            } else {
8713                None
8714            };
8715            return Ok(Some(Sample {
8716                method: SampleMethod::Bernoulli,
8717                size,
8718                seed: None,
8719                offset,
8720                unit_after_size: false,
8721                use_sample_keyword,
8722                explicit_method: false,
8723                method_before_size: false,
8724                use_seed_keyword: false,
8725                bucket_numerator: None,
8726                bucket_denominator: None,
8727                bucket_field: None,
8728                is_using_sample: false,
8729                is_percent: false,
8730                suppress_method_output: false,
8731            }));
8732        }
8733
8734        // Parse method name (optional for table-level TABLESAMPLE)
8735        let (method, explicit_method, method_before_size) = if self.check_identifier("RESERVOIR") {
8736            self.skip();
8737            (SampleMethod::Reservoir, true, true)
8738        } else if self.match_token(TokenType::Bernoulli) {
8739            (SampleMethod::Bernoulli, true, true)
8740        } else if self.match_token(TokenType::System) {
8741            (SampleMethod::System, true, true)
8742        } else if self.match_token(TokenType::Block) {
8743            (SampleMethod::Block, true, true)
8744        } else if self.match_token(TokenType::Row) {
8745            (SampleMethod::Row, true, true)
8746        } else {
8747            // No explicit method - default to Bernoulli internally but track as not explicit
8748            (SampleMethod::Bernoulli, false, false)
8749        };
8750
8751        // Parse (size [PERCENT|ROWS])
8752        self.expect(TokenType::LParen)?;
8753
8754        // Check for BUCKET syntax: TABLESAMPLE (BUCKET 1 OUT OF 5 [ON col])
8755        if self.match_identifier("BUCKET") {
8756            let bucket_numerator = self.parse_primary()?;
8757            self.match_identifier("OUT");
8758            self.match_token(TokenType::Of);
8759            let bucket_denominator = self.parse_primary()?;
8760            let bucket_field = if self.match_token(TokenType::On) {
8761                Some(Box::new(self.parse_primary()?))
8762            } else {
8763                None
8764            };
8765            self.expect(TokenType::RParen)?;
8766            return Ok(Some(Sample {
8767                method: SampleMethod::Bucket,
8768                size: bucket_numerator.clone(),
8769                seed: None,
8770                offset: None,
8771                unit_after_size: false,
8772                use_sample_keyword,
8773                explicit_method: true,
8774                method_before_size: false,
8775                use_seed_keyword: false,
8776                bucket_numerator: Some(Box::new(bucket_numerator)),
8777                bucket_denominator: Some(Box::new(bucket_denominator)),
8778                bucket_field,
8779                is_using_sample: false,
8780                is_percent: false,
8781                suppress_method_output: false,
8782            }));
8783        }
8784
8785        let size = self.parse_unary()?;
8786
8787        // Check for PERCENT/ROWS suffix or % symbol
8788        let (method, unit_after_size, is_percent) =
8789            if self.check(TokenType::Percent) && self.peek().text.eq_ignore_ascii_case("PERCENT") {
8790                self.skip();
8791                // If no explicit method, use Percent to represent "PERCENT" unit
8792                if explicit_method {
8793                    (method, true, true)
8794                } else {
8795                    (SampleMethod::Percent, true, true)
8796                }
8797            } else if self.match_token(TokenType::Rows) {
8798                // If no explicit method, use Row to represent "ROWS" unit
8799                if explicit_method {
8800                    (method, true, false)
8801                } else {
8802                    (SampleMethod::Row, true, false)
8803                }
8804            } else if self.check(TokenType::Percent) && self.peek().text == "%" {
8805                // 20% -> consume the %, treat as PERCENT unit
8806                self.skip();
8807                if explicit_method {
8808                    (method, true, true)
8809                } else {
8810                    (SampleMethod::Percent, true, true)
8811                }
8812            } else {
8813                (method, false, false)
8814            };
8815
8816        self.expect(TokenType::RParen)?;
8817
8818        // Optional SEED/REPEATABLE
8819        let (seed, use_seed_keyword) = if self.match_token(TokenType::Seed) {
8820            self.expect(TokenType::LParen)?;
8821            let seed_value = self.parse_expression()?;
8822            self.expect(TokenType::RParen)?;
8823            (Some(seed_value), true)
8824        } else if self.match_token(TokenType::Repeatable) {
8825            self.expect(TokenType::LParen)?;
8826            let seed_value = self.parse_expression()?;
8827            self.expect(TokenType::RParen)?;
8828            (Some(seed_value), false)
8829        } else {
8830            (None, false)
8831        };
8832
8833        Ok(Some(Sample {
8834            method,
8835            size,
8836            seed,
8837            offset: None,
8838            unit_after_size,
8839            use_sample_keyword,
8840            explicit_method,
8841            method_before_size,
8842            use_seed_keyword,
8843            bucket_numerator: None,
8844            bucket_denominator: None,
8845            bucket_field: None,
8846            is_using_sample: false, // table-level uses TABLESAMPLE/SAMPLE keyword, not USING SAMPLE
8847            is_percent,
8848            suppress_method_output: false,
8849        }))
8850    }
8851
8852    /// Parse set operations (UNION, INTERSECT, EXCEPT)
8853    fn parse_set_operation(&mut self, left: Expression) -> Result<Expression> {
8854        // Check for BigQuery set operation modifiers BEFORE the set operation keyword
8855        // Pattern: SELECT ... [INNER|LEFT|RIGHT|FULL] UNION/INTERSECT/EXCEPT ...
8856        let (side, kind) = self.parse_set_operation_side_kind();
8857
8858        // Capture leading comments from the set operation keyword token (e.g., /*x*/ before UNION).
8859        // These comments appeared on a new line between the left SELECT and the set operation keyword.
8860        let set_op_leading_comments = if self.check(TokenType::Union)
8861            || self.check(TokenType::Intersect)
8862            || self.check(TokenType::Except)
8863        {
8864            self.current_leading_comments().to_vec()
8865        } else {
8866            Vec::new()
8867        };
8868
8869        // Wrap left expression with comments if needed
8870        let left = if !set_op_leading_comments.is_empty() {
8871            Expression::Annotated(Box::new(Annotated {
8872                this: left,
8873                trailing_comments: set_op_leading_comments,
8874            }))
8875        } else {
8876            left
8877        };
8878
8879        if self.match_token(TokenType::Union) {
8880            let all = self.match_token(TokenType::All);
8881            let distinct = if !all {
8882                self.match_token(TokenType::Distinct)
8883            } else {
8884                false
8885            };
8886
8887            // Parse STRICT CORRESPONDING, CORRESPONDING, BY NAME modifiers
8888            let (by_name, strict, corresponding, on_columns) =
8889                self.parse_set_operation_corresponding()?;
8890
8891            // If CORRESPONDING (without STRICT) is present and no explicit side/kind, default kind to INNER
8892            // STRICT CORRESPONDING does NOT set kind to INNER
8893            let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8894                Some("INNER".to_string())
8895            } else {
8896                kind
8897            };
8898
8899            let right = self.parse_select_or_paren_select()?;
8900            // Check for chained set operations first
8901            let mut result = Expression::Union(Box::new(Union {
8902                left,
8903                right,
8904                all,
8905                distinct,
8906                with: None,
8907                order_by: None,
8908                limit: None,
8909                offset: None,
8910                distribute_by: None,
8911                sort_by: None,
8912                cluster_by: None,
8913                by_name,
8914                side,
8915                kind,
8916                corresponding,
8917                strict,
8918                on_columns,
8919            }));
8920            result = self.parse_set_operation(result)?;
8921            // Parse ORDER BY, LIMIT, OFFSET for the outermost set operation
8922            self.parse_set_operation_modifiers(&mut result)?;
8923            Ok(result)
8924        } else if self.match_token(TokenType::Intersect) {
8925            let all = self.match_token(TokenType::All);
8926            let distinct = if !all {
8927                self.match_token(TokenType::Distinct)
8928            } else {
8929                false
8930            };
8931
8932            // Parse STRICT CORRESPONDING, CORRESPONDING, BY NAME modifiers
8933            let (by_name, strict, corresponding, on_columns) =
8934                self.parse_set_operation_corresponding()?;
8935
8936            // If CORRESPONDING (without STRICT) is present and no explicit side/kind, default kind to INNER
8937            // STRICT CORRESPONDING does NOT set kind to INNER
8938            let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8939                Some("INNER".to_string())
8940            } else {
8941                kind
8942            };
8943
8944            let right = self.parse_select_or_paren_select()?;
8945            let mut result = Expression::Intersect(Box::new(Intersect {
8946                left,
8947                right,
8948                all,
8949                distinct,
8950                with: None,
8951                order_by: None,
8952                limit: None,
8953                offset: None,
8954                distribute_by: None,
8955                sort_by: None,
8956                cluster_by: None,
8957                by_name,
8958                side,
8959                kind,
8960                corresponding,
8961                strict,
8962                on_columns,
8963            }));
8964            result = self.parse_set_operation(result)?;
8965            self.parse_set_operation_modifiers(&mut result)?;
8966            Ok(result)
8967        } else if self.match_token(TokenType::Except) {
8968            let all = self.match_token(TokenType::All);
8969            let distinct = if !all {
8970                self.match_token(TokenType::Distinct)
8971            } else {
8972                false
8973            };
8974
8975            // Parse STRICT CORRESPONDING, CORRESPONDING, BY NAME modifiers
8976            let (by_name, strict, corresponding, on_columns) =
8977                self.parse_set_operation_corresponding()?;
8978
8979            // If CORRESPONDING (without STRICT) is present and no explicit side/kind, default kind to INNER
8980            // STRICT CORRESPONDING does NOT set kind to INNER
8981            let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8982                Some("INNER".to_string())
8983            } else {
8984                kind
8985            };
8986
8987            let right = self.parse_select_or_paren_select()?;
8988            let mut result = Expression::Except(Box::new(Except {
8989                left,
8990                right,
8991                all,
8992                distinct,
8993                with: None,
8994                order_by: None,
8995                limit: None,
8996                offset: None,
8997                distribute_by: None,
8998                sort_by: None,
8999                cluster_by: None,
9000                by_name,
9001                side,
9002                kind,
9003                corresponding,
9004                strict,
9005                on_columns,
9006            }));
9007            result = self.parse_set_operation(result)?;
9008            self.parse_set_operation_modifiers(&mut result)?;
9009            Ok(result)
9010        } else if side.is_some() || kind.is_some() {
9011            // We parsed side/kind but didn't find a set operation - this is an error
9012            Err(self
9013                .parse_error("Expected UNION, INTERSECT, or EXCEPT after set operation modifier"))
9014        } else {
9015            Ok(left)
9016        }
9017    }
9018
9019    /// Parse BigQuery set operation side (LEFT, RIGHT, FULL) and kind (INNER)
9020    /// These modifiers appear BEFORE the UNION/INTERSECT/EXCEPT keyword
9021    fn parse_set_operation_side_kind(&mut self) -> (Option<String>, Option<String>) {
9022        let mut side = None;
9023        let mut kind = None;
9024
9025        // Check for side: LEFT, RIGHT, FULL (reusing join side tokens)
9026        if self.check(TokenType::Left)
9027            || self.check(TokenType::Right)
9028            || self.check(TokenType::Full)
9029        {
9030            // Only consume if followed by UNION/INTERSECT/EXCEPT (or INNER which would be followed by them)
9031            let saved = self.current;
9032            let side_token = self.advance();
9033            let side_text = side_token.text.to_ascii_uppercase();
9034
9035            // Check if followed by set operation or INNER
9036            if self.check(TokenType::Union)
9037                || self.check(TokenType::Intersect)
9038                || self.check(TokenType::Except)
9039                || self.check(TokenType::Inner)
9040            {
9041                side = Some(side_text);
9042            } else {
9043                // Not a set operation modifier, backtrack
9044                self.current = saved;
9045                return (None, None);
9046            }
9047        }
9048
9049        // Check for kind: INNER
9050        if self.check(TokenType::Inner) {
9051            let saved = self.current;
9052            self.skip(); // consume INNER
9053
9054            // Check if followed by set operation
9055            if self.check(TokenType::Union)
9056                || self.check(TokenType::Intersect)
9057                || self.check(TokenType::Except)
9058            {
9059                kind = Some("INNER".to_string());
9060            } else {
9061                // Not a set operation modifier, backtrack
9062                self.current = saved;
9063                if side.is_some() {
9064                    // We already consumed a side token, need to backtrack that too
9065                    self.current = saved - 1;
9066                }
9067                return (None, None);
9068            }
9069        }
9070
9071        (side, kind)
9072    }
9073
9074    /// Parse CORRESPONDING/STRICT CORRESPONDING/BY NAME modifiers after ALL/DISTINCT
9075    /// Returns (by_name, strict, corresponding, on_columns)
9076    fn parse_set_operation_corresponding(&mut self) -> Result<(bool, bool, bool, Vec<Expression>)> {
9077        let mut by_name = false;
9078        let mut strict = false;
9079        let mut corresponding = false;
9080        let mut on_columns = Vec::new();
9081
9082        // Check for BY NAME (DuckDB style)
9083        if self.match_token(TokenType::By) && self.match_identifier("NAME") {
9084            by_name = true;
9085        }
9086        // Check for STRICT CORRESPONDING (BigQuery style)
9087        else if self.match_identifier("STRICT") {
9088            if self.match_identifier("CORRESPONDING") {
9089                strict = true;
9090                corresponding = true;
9091            } else {
9092                // STRICT without CORRESPONDING - backtrack
9093                self.current -= 1;
9094            }
9095        }
9096        // Check for CORRESPONDING (BigQuery style)
9097        else if self.match_identifier("CORRESPONDING") {
9098            corresponding = true;
9099        }
9100
9101        // If CORRESPONDING is set, check for BY (columns)
9102        if corresponding && self.match_token(TokenType::By) {
9103            self.expect(TokenType::LParen)?;
9104            on_columns = self
9105                .parse_identifier_list()?
9106                .into_iter()
9107                .map(|id| {
9108                    Expression::boxed_column(Column {
9109                        name: id,
9110                        table: None,
9111                        join_mark: false,
9112                        trailing_comments: Vec::new(),
9113                        span: None,
9114                        inferred_type: None,
9115                    })
9116                })
9117                .collect();
9118            self.expect(TokenType::RParen)?;
9119        }
9120
9121        Ok((by_name, strict, corresponding, on_columns))
9122    }
9123
9124    /// Parse ORDER BY, LIMIT, OFFSET modifiers for set operations
9125    fn parse_set_operation_modifiers(&mut self, expr: &mut Expression) -> Result<()> {
9126        // Parse ORDER BY
9127        let order_by = if self.match_token(TokenType::Order) {
9128            self.expect(TokenType::By)?;
9129            Some(self.parse_order_by()?)
9130        } else {
9131            None
9132        };
9133
9134        // Parse LIMIT
9135        let limit = if self.match_token(TokenType::Limit) {
9136            Some(Box::new(self.parse_expression()?))
9137        } else {
9138            None
9139        };
9140
9141        // Parse OFFSET
9142        let offset = if self.match_token(TokenType::Offset) {
9143            Some(Box::new(self.parse_expression()?))
9144        } else {
9145            None
9146        };
9147
9148        // Apply modifiers to the outermost set operation
9149        match expr {
9150            Expression::Union(ref mut union) => {
9151                if order_by.is_some() {
9152                    union.order_by = order_by;
9153                }
9154                if limit.is_some() {
9155                    union.limit = limit;
9156                }
9157                if offset.is_some() {
9158                    union.offset = offset;
9159                }
9160            }
9161            Expression::Intersect(ref mut intersect) => {
9162                if order_by.is_some() {
9163                    intersect.order_by = order_by;
9164                }
9165                if limit.is_some() {
9166                    intersect.limit = limit;
9167                }
9168                if offset.is_some() {
9169                    intersect.offset = offset;
9170                }
9171            }
9172            Expression::Except(ref mut except) => {
9173                if order_by.is_some() {
9174                    except.order_by = order_by;
9175                }
9176                if limit.is_some() {
9177                    except.limit = limit;
9178                }
9179                if offset.is_some() {
9180                    except.offset = offset;
9181                }
9182            }
9183            _ => {}
9184        }
9185        Ok(())
9186    }
9187
9188    /// Parse either a SELECT statement or a parenthesized SELECT/set operation
9189    fn parse_select_or_paren_select(&mut self) -> Result<Expression> {
9190        if self.match_token(TokenType::LParen) {
9191            // Could be (SELECT ...) or ((SELECT ...) UNION ...) or (FROM ...) for DuckDB
9192            if self.check(TokenType::Select)
9193                || self.check(TokenType::With)
9194                || self.check(TokenType::From)
9195            {
9196                let query = self.parse_statement()?;
9197                self.expect(TokenType::RParen)?;
9198                // Handle optional alias after subquery: (SELECT 1) AS a
9199                let alias = if self.match_token(TokenType::As) {
9200                    Some(Identifier::new(self.expect_identifier()?))
9201                } else {
9202                    None
9203                };
9204                // Wrap in Subquery to preserve parentheses
9205                Ok(Expression::Subquery(Box::new(Subquery {
9206                    this: query,
9207                    alias,
9208                    column_aliases: Vec::new(),
9209                    order_by: None,
9210                    limit: None,
9211                    offset: None,
9212                    lateral: false,
9213                    modifiers_inside: false,
9214                    trailing_comments: Vec::new(),
9215                    distribute_by: None,
9216                    sort_by: None,
9217                    cluster_by: None,
9218                    inferred_type: None,
9219                })))
9220            } else if self.check(TokenType::LParen) {
9221                // Nested parentheses like ((SELECT ...))
9222                let inner = self.parse_select_or_paren_select()?;
9223                // Check for set operations inside the parens
9224                let result = self.parse_set_operation(inner)?;
9225                self.expect(TokenType::RParen)?;
9226                // Handle optional alias after subquery
9227                let alias = if self.match_token(TokenType::As) {
9228                    Some(Identifier::new(self.expect_identifier()?))
9229                } else {
9230                    None
9231                };
9232                // Wrap in Subquery to preserve parentheses
9233                Ok(Expression::Subquery(Box::new(Subquery {
9234                    this: result,
9235                    alias,
9236                    column_aliases: Vec::new(),
9237                    order_by: None,
9238                    limit: None,
9239                    offset: None,
9240                    lateral: false,
9241                    modifiers_inside: false,
9242                    trailing_comments: Vec::new(),
9243                    distribute_by: None,
9244                    sort_by: None,
9245                    cluster_by: None,
9246                    inferred_type: None,
9247                })))
9248            } else {
9249                Err(self.parse_error("Expected SELECT or ( after ("))
9250            }
9251        } else if self.check(TokenType::From) {
9252            // DuckDB FROM-first syntax without parentheses: ... UNION FROM t
9253            self.parse_from_first_query()
9254        } else if self.check(TokenType::With) {
9255            // WITH CTE as right-hand side of UNION/INTERSECT/EXCEPT
9256            self.parse_statement()
9257        } else {
9258            self.parse_select()
9259        }
9260    }
9261
9262    /// Parse INSERT statement
9263    fn parse_insert(&mut self) -> Result<Expression> {
9264        let insert_token = self.expect(TokenType::Insert)?;
9265        let leading_comments = insert_token.comments;
9266
9267        // Parse query hint /*+ ... */ if present (Oracle: INSERT /*+ APPEND */ INTO ...)
9268        let hint = if self.check(TokenType::Hint) {
9269            Some(self.parse_hint()?)
9270        } else {
9271            None
9272        };
9273
9274        // Handle SQLite conflict action: INSERT OR ABORT|FAIL|IGNORE|REPLACE|ROLLBACK INTO
9275        let conflict_action = if self.match_token(TokenType::Or) {
9276            if self.match_identifier("ABORT") {
9277                Some("ABORT".to_string())
9278            } else if self.match_identifier("FAIL") {
9279                Some("FAIL".to_string())
9280            } else if self.match_token(TokenType::Ignore) {
9281                Some("IGNORE".to_string())
9282            } else if self.match_token(TokenType::Replace) {
9283                Some("REPLACE".to_string())
9284            } else if self.match_token(TokenType::Rollback) {
9285                Some("ROLLBACK".to_string())
9286            } else {
9287                return Err(self.parse_error(
9288                    "Expected ABORT, FAIL, IGNORE, REPLACE, or ROLLBACK after INSERT OR",
9289                ));
9290            }
9291        } else {
9292            None
9293        };
9294
9295        // Handle INSERT IGNORE (MySQL)
9296        let ignore = conflict_action.is_none() && self.match_token(TokenType::Ignore);
9297
9298        // Handle OVERWRITE for Hive/Spark: INSERT OVERWRITE TABLE ...
9299        let overwrite = self.match_token(TokenType::Overwrite);
9300
9301        // Handle Oracle multi-table INSERT: INSERT ALL/FIRST ...
9302        // Must check before OVERWRITE handling since these are mutually exclusive
9303        if !overwrite && (self.match_token(TokenType::All) || self.match_token(TokenType::First)) {
9304            if let Some(multi_insert) = self.parse_multitable_inserts(leading_comments.clone())? {
9305                return Ok(multi_insert);
9306            }
9307        }
9308
9309        // Handle INTO or TABLE (OVERWRITE requires TABLE, INTO is standard)
9310        // Also handle INSERT OVERWRITE [LOCAL] DIRECTORY 'path'
9311        let local_directory = overwrite && self.match_token(TokenType::Local);
9312        let is_directory = (overwrite || local_directory) && self.match_identifier("DIRECTORY");
9313
9314        if is_directory {
9315            // INSERT OVERWRITE [LOCAL] DIRECTORY 'path' [ROW FORMAT ...] SELECT ...
9316            let path = self.expect_string()?;
9317            // Parse optional ROW FORMAT clause
9318            let row_format = if self.match_keywords(&[TokenType::Row, TokenType::Format]) {
9319                // ROW FORMAT DELIMITED ...
9320                let delimited = self.match_identifier("DELIMITED");
9321                let mut fields_terminated_by = None;
9322                let mut collection_items_terminated_by = None;
9323                let mut map_keys_terminated_by = None;
9324                let mut lines_terminated_by = None;
9325                let mut null_defined_as = None;
9326
9327                // Parse the various TERMINATED BY clauses
9328                loop {
9329                    if self.match_identifier("FIELDS") || self.match_identifier("FIELD") {
9330                        self.match_identifier("TERMINATED");
9331                        self.match_token(TokenType::By);
9332                        fields_terminated_by = Some(self.expect_string()?);
9333                    } else if self.match_identifier("COLLECTION") {
9334                        self.match_identifier("ITEMS");
9335                        self.match_identifier("TERMINATED");
9336                        self.match_token(TokenType::By);
9337                        collection_items_terminated_by = Some(self.expect_string()?);
9338                    } else if self.match_identifier("MAP") {
9339                        self.match_identifier("KEYS");
9340                        self.match_identifier("TERMINATED");
9341                        self.match_token(TokenType::By);
9342                        map_keys_terminated_by = Some(self.expect_string()?);
9343                    } else if self.match_identifier("LINES") {
9344                        self.match_identifier("TERMINATED");
9345                        self.match_token(TokenType::By);
9346                        lines_terminated_by = Some(self.expect_string()?);
9347                    } else if self.match_token(TokenType::Null) {
9348                        self.match_identifier("DEFINED");
9349                        self.match_token(TokenType::As);
9350                        null_defined_as = Some(self.expect_string()?);
9351                    } else {
9352                        break;
9353                    }
9354                }
9355
9356                Some(RowFormat {
9357                    delimited,
9358                    fields_terminated_by,
9359                    collection_items_terminated_by,
9360                    map_keys_terminated_by,
9361                    lines_terminated_by,
9362                    null_defined_as,
9363                })
9364            } else {
9365                None
9366            };
9367
9368            // Parse optional STORED AS clause
9369            let stored_as = if self.match_identifier("STORED") {
9370                self.expect(TokenType::As)?;
9371                Some(self.expect_identifier()?)
9372            } else {
9373                None
9374            };
9375
9376            // Parse the SELECT query
9377            let query = self.parse_statement()?;
9378
9379            return Ok(Expression::Insert(Box::new(Insert {
9380                table: TableRef::new(""),
9381                columns: Vec::new(),
9382                values: Vec::new(),
9383                query: Some(query),
9384                overwrite,
9385                partition: Vec::new(),
9386                directory: Some(DirectoryInsert {
9387                    local: local_directory,
9388                    path,
9389                    row_format,
9390                    stored_as,
9391                }),
9392                returning: Vec::new(),
9393                output: None,
9394                on_conflict: None,
9395                leading_comments,
9396                if_exists: false,
9397                with: None,
9398                ignore,
9399                source_alias: None,
9400                alias: None,
9401                alias_explicit_as: false,
9402                default_values: false,
9403                by_name: false,
9404                conflict_action: conflict_action.clone(),
9405                is_replace: false,
9406                replace_where: None,
9407                source: None,
9408                hint: hint.clone(),
9409                function_target: None,
9410                partition_by: None,
9411                settings: Vec::new(),
9412            })));
9413        }
9414
9415        if overwrite {
9416            // OVERWRITE is typically followed by TABLE
9417            self.match_token(TokenType::Table);
9418        } else {
9419            self.expect(TokenType::Into)?;
9420            // Optional TABLE keyword after INTO
9421            self.match_token(TokenType::Table);
9422        }
9423
9424        // ClickHouse: INSERT INTO [TABLE] FUNCTION func_name(args...)
9425        let mut function_target: Option<Box<Expression>> = None;
9426        if self.match_token(TokenType::Function) {
9427            // Parse function call: func_name(args...)
9428            let func_name = self.expect_identifier_or_keyword()?;
9429            self.expect(TokenType::LParen)?;
9430            let args = if self.check(TokenType::RParen) {
9431                Vec::new()
9432            } else {
9433                self.parse_expression_list()?
9434            };
9435            self.expect(TokenType::RParen)?;
9436            function_target = Some(Box::new(Expression::Function(Box::new(Function {
9437                name: func_name,
9438                args,
9439                distinct: false,
9440                trailing_comments: Vec::new(),
9441                use_bracket_syntax: false,
9442                no_parens: false,
9443                quoted: false,
9444                span: None,
9445                inferred_type: None,
9446            }))));
9447        }
9448
9449        let table_name = if function_target.is_some() {
9450            // For FUNCTION targets, use empty table name
9451            Identifier::new(String::new())
9452        } else {
9453            // Allow keywords (like TABLE) as table names in INSERT statements
9454            self.expect_identifier_or_keyword_with_quoted()?
9455        };
9456        // Handle qualified table names like a.b
9457        let table = if self.match_token(TokenType::Dot) {
9458            let schema = table_name;
9459            let name = self.expect_identifier_or_keyword_with_quoted()?;
9460            let trailing_comments = self.previous_trailing_comments().to_vec();
9461            TableRef {
9462                name,
9463                schema: Some(schema),
9464                catalog: None,
9465                alias: None,
9466                alias_explicit_as: false,
9467                column_aliases: Vec::new(),
9468                leading_comments: Vec::new(),
9469                trailing_comments,
9470                when: None,
9471                only: false,
9472                final_: false,
9473                table_sample: None,
9474                hints: Vec::new(),
9475                system_time: None,
9476                partitions: Vec::new(),
9477                identifier_func: None,
9478                changes: None,
9479                version: None,
9480                span: None,
9481            }
9482        } else {
9483            let trailing_comments = self.previous_trailing_comments().to_vec();
9484            TableRef {
9485                name: table_name,
9486                schema: None,
9487                catalog: None,
9488                alias: None,
9489                alias_explicit_as: false,
9490                column_aliases: Vec::new(),
9491                leading_comments: Vec::new(),
9492                when: None,
9493                only: false,
9494                final_: false,
9495                table_sample: None,
9496                hints: Vec::new(),
9497                system_time: None,
9498                trailing_comments,
9499                partitions: Vec::new(),
9500                identifier_func: None,
9501                changes: None,
9502                version: None,
9503                span: None,
9504            }
9505        };
9506
9507        // Optional alias (PostgreSQL: INSERT INTO table AS t(...), Oracle: INSERT INTO table t ...)
9508        let (alias, alias_explicit_as) = if self.match_token(TokenType::As) {
9509            (Some(Identifier::new(self.expect_identifier()?)), true)
9510        } else if self.is_identifier_token()
9511            && !self.check(TokenType::Values)
9512            && !self.check(TokenType::Select)
9513            && !self.check(TokenType::Default)
9514            && !self.check(TokenType::By)
9515            && !self.check(TokenType::Partition)
9516            && !self.check(TokenType::Output)
9517            && !self.check(TokenType::If)
9518            && !self.check(TokenType::Replace)
9519            && !self.check(TokenType::Table)
9520            && !self.check(TokenType::LParen)
9521        {
9522            // Implicit alias without AS (e.g., INSERT INTO dest d VALUES ...)
9523            (Some(Identifier::new(self.expect_identifier()?)), false)
9524        } else {
9525            (None, false)
9526        };
9527
9528        // Optional IF EXISTS (Hive)
9529        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
9530
9531        // Optional REPLACE WHERE clause (Databricks): INSERT INTO a REPLACE WHERE cond VALUES ...
9532        let replace_where =
9533            if self.match_token(TokenType::Replace) && self.match_token(TokenType::Where) {
9534                Some(Box::new(self.parse_or()?))
9535            } else {
9536                None
9537            };
9538
9539        // Optional PARTITION clause
9540        // ClickHouse: PARTITION BY expr (no parens)
9541        // Hive/Spark: PARTITION (col1 = val1, col2)
9542        let mut partition_by_expr: Option<Box<Expression>> = None;
9543        let partition = if self.check(TokenType::Partition) && self.check_next(TokenType::By) {
9544            // ClickHouse PARTITION BY expr
9545            self.skip(); // consume PARTITION
9546            self.skip(); // consume BY
9547            partition_by_expr = Some(Box::new(self.parse_expression()?));
9548            Vec::new()
9549        } else if self.match_token(TokenType::Partition) {
9550            self.expect(TokenType::LParen)?;
9551            let mut parts = Vec::new();
9552            loop {
9553                let col = Identifier::new(self.expect_identifier()?);
9554                let value = if self.match_token(TokenType::Eq) {
9555                    Some(self.parse_expression()?)
9556                } else {
9557                    None
9558                };
9559                parts.push((col, value));
9560                if !self.match_token(TokenType::Comma) {
9561                    break;
9562                }
9563            }
9564            self.expect(TokenType::RParen)?;
9565            parts
9566        } else {
9567            Vec::new()
9568        };
9569
9570        // ClickHouse: SETTINGS key = val, ...
9571        let insert_settings = if self.match_token(TokenType::Settings) {
9572            let mut settings = Vec::new();
9573            loop {
9574                settings.push(self.parse_expression()?);
9575                if !self.match_token(TokenType::Comma) {
9576                    break;
9577                }
9578            }
9579            settings
9580        } else {
9581            Vec::new()
9582        };
9583
9584        // Optional column list OR parenthesized subquery
9585        // We need to check if ( is followed by SELECT/WITH (subquery) or identifiers (column list)
9586        let columns = if self.check(TokenType::LParen) {
9587            // Look ahead to see if this is a subquery or column list
9588            if self
9589                .peek_nth(1)
9590                .map(|t| t.token_type == TokenType::Select || t.token_type == TokenType::With)
9591                .unwrap_or(false)
9592            {
9593                // This is a parenthesized subquery, not a column list
9594                Vec::new()
9595            } else if matches!(
9596                self.config.dialect,
9597                Some(crate::dialects::DialectType::ClickHouse)
9598            ) && {
9599                // ClickHouse: INSERT INTO t (*), t(* EXCEPT ...), t(table.* EXCEPT ...), t(COLUMNS('pattern') EXCEPT ...)
9600                let peek1 = self.peek_nth(1).map(|t| t.token_type);
9601                peek1 == Some(TokenType::Star)
9602                    || (peek1 == Some(TokenType::Var)
9603                        && self.peek_nth(2).map(|t| t.token_type) == Some(TokenType::Dot)
9604                        && self.peek_nth(3).map(|t| t.token_type) == Some(TokenType::Star))
9605                    || (peek1 == Some(TokenType::Var)
9606                        && self
9607                            .peek_nth(1)
9608                            .map(|t| t.text.eq_ignore_ascii_case("COLUMNS"))
9609                            .unwrap_or(false))
9610            } {
9611                // Consume balanced parens and skip entire column specification
9612                self.skip(); // consume (
9613                let mut depth = 1i32;
9614                while !self.is_at_end() && depth > 0 {
9615                    if self.check(TokenType::LParen) {
9616                        depth += 1;
9617                    }
9618                    if self.check(TokenType::RParen) {
9619                        depth -= 1;
9620                        if depth == 0 {
9621                            break;
9622                        }
9623                    }
9624                    self.skip();
9625                }
9626                self.expect(TokenType::RParen)?;
9627                Vec::new() // Treat as "all columns"
9628            } else {
9629                self.skip(); // consume (
9630                let cols = self.parse_identifier_list()?;
9631                self.expect(TokenType::RParen)?;
9632                cols
9633            }
9634        } else {
9635            Vec::new()
9636        };
9637
9638        // Parse OUTPUT clause (TSQL)
9639        let output = if self.match_token(TokenType::Output) {
9640            Some(self.parse_output_clause()?)
9641        } else {
9642            None
9643        };
9644
9645        // Check for BY NAME (DuckDB): INSERT INTO x BY NAME SELECT ...
9646        let by_name = self.match_token(TokenType::By) && self.match_identifier("NAME");
9647
9648        // Check for DEFAULT VALUES (PostgreSQL)
9649        let default_values =
9650            self.match_token(TokenType::Default) && self.match_token(TokenType::Values);
9651
9652        // VALUES or SELECT or TABLE source (Hive/Spark) or DEFAULT VALUES (already consumed above)
9653        let (values, query) = if default_values {
9654            // DEFAULT VALUES: no values or query
9655            (Vec::new(), None)
9656        } else if matches!(
9657            self.config.dialect,
9658            Some(crate::dialects::DialectType::ClickHouse)
9659        ) && self.check(TokenType::Format)
9660            && self.peek_nth(1).is_some_and(|t| {
9661                !t.text.eq_ignore_ascii_case("VALUES")
9662                    && (t.token_type == TokenType::Var || t.token_type == TokenType::Identifier)
9663            })
9664        {
9665            // ClickHouse: FORMAT <format_name> followed by raw data (CSV, JSON, TSV, etc.)
9666            // Skip everything to next semicolon or end — the data is not SQL
9667            self.skip(); // consume FORMAT
9668            let format_name = self.advance().text.clone(); // consume format name
9669                                                           // Consume all remaining tokens until semicolon (raw data)
9670            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
9671                self.skip();
9672            }
9673            // Store as empty values with the format name in the query as a command
9674            (
9675                Vec::new(),
9676                Some(Expression::Command(Box::new(crate::expressions::Command {
9677                    this: format!("FORMAT {}", format_name),
9678                }))),
9679            )
9680        } else if matches!(
9681            self.config.dialect,
9682            Some(crate::dialects::DialectType::ClickHouse)
9683        ) && self.match_text_seq(&["FORMAT", "VALUES"])
9684        {
9685            let mut all_values = Vec::new();
9686
9687            loop {
9688                self.expect(TokenType::LParen)?;
9689                let row = self.parse_expression_list()?;
9690                self.expect(TokenType::RParen)?;
9691                all_values.push(row);
9692
9693                if !self.match_token(TokenType::Comma) {
9694                    break;
9695                }
9696            }
9697
9698            (all_values, None)
9699        } else if self.match_token(TokenType::Values) {
9700            let mut all_values = Vec::new();
9701
9702            // ClickHouse: INSERT INTO t VALUES; — empty VALUES (clientError expected)
9703            if matches!(
9704                self.config.dialect,
9705                Some(crate::dialects::DialectType::ClickHouse)
9706            ) && (self.check(TokenType::Semicolon) || self.is_at_end())
9707            {
9708                // Return empty INSERT as Command to avoid needing all Insert fields
9709                return Ok(Expression::Command(Box::new(crate::expressions::Command {
9710                    this: "INSERT INTO VALUES".to_string(),
9711                })));
9712            }
9713
9714            // ClickHouse: allow bare VALUES without parens: VALUES 1, 2, 3
9715            if matches!(
9716                self.config.dialect,
9717                Some(crate::dialects::DialectType::ClickHouse)
9718            ) && !self.check(TokenType::LParen)
9719            {
9720                loop {
9721                    let val = self.parse_expression()?;
9722                    all_values.push(vec![val]);
9723                    if !self.match_token(TokenType::Comma) {
9724                        break;
9725                    }
9726                }
9727            } else {
9728                loop {
9729                    self.expect(TokenType::LParen)?;
9730                    // ClickHouse: allow empty VALUES () — empty tuple
9731                    let row = if self.check(TokenType::RParen) {
9732                        Vec::new()
9733                    } else {
9734                        self.parse_values_expression_list()?
9735                    };
9736                    self.expect(TokenType::RParen)?;
9737                    all_values.push(row);
9738
9739                    if !self.match_token(TokenType::Comma) {
9740                        // ClickHouse: allow tuples without commas: VALUES (1) (2) (3)
9741                        if matches!(
9742                            self.config.dialect,
9743                            Some(crate::dialects::DialectType::ClickHouse)
9744                        ) && self.check(TokenType::LParen)
9745                        {
9746                            continue;
9747                        }
9748                        break;
9749                    }
9750                    // ClickHouse: allow trailing comma after last tuple
9751                    if matches!(
9752                        self.config.dialect,
9753                        Some(crate::dialects::DialectType::ClickHouse)
9754                    ) && !self.check(TokenType::LParen)
9755                    {
9756                        break;
9757                    }
9758                }
9759            } // close else (parenthesized values)
9760
9761            (all_values, None)
9762        } else if self.check(TokenType::Table) {
9763            // Hive/Spark: INSERT OVERWRITE TABLE target TABLE source
9764            // The TABLE keyword here indicates source table, not a subquery
9765            (Vec::new(), None)
9766        } else {
9767            (Vec::new(), Some(self.parse_statement()?))
9768        };
9769
9770        // Parse source table (Hive/Spark): INSERT OVERWRITE TABLE target TABLE source
9771        let source = if self.match_token(TokenType::Table) {
9772            // Parse source table reference (similar to target table parsing)
9773            let source_name = self.expect_identifier_with_quoted()?;
9774            let source_table = if self.match_token(TokenType::Dot) {
9775                let schema = source_name;
9776                let name = self.expect_identifier_with_quoted()?;
9777                let trailing_comments = self.previous_trailing_comments().to_vec();
9778                TableRef {
9779                    name,
9780                    schema: Some(schema),
9781                    catalog: None,
9782                    alias: None,
9783                    alias_explicit_as: false,
9784                    column_aliases: Vec::new(),
9785                    leading_comments: Vec::new(),
9786                    trailing_comments,
9787                    when: None,
9788                    only: false,
9789                    final_: false,
9790                    table_sample: None,
9791                    hints: Vec::new(),
9792                    system_time: None,
9793                    partitions: Vec::new(),
9794                    identifier_func: None,
9795                    changes: None,
9796                    version: None,
9797                    span: None,
9798                }
9799            } else {
9800                let trailing_comments = self.previous_trailing_comments().to_vec();
9801                TableRef {
9802                    name: source_name,
9803                    schema: None,
9804                    catalog: None,
9805                    alias: None,
9806                    alias_explicit_as: false,
9807                    column_aliases: Vec::new(),
9808                    leading_comments: Vec::new(),
9809                    trailing_comments,
9810                    when: None,
9811                    only: false,
9812                    final_: false,
9813                    table_sample: None,
9814                    hints: Vec::new(),
9815                    system_time: None,
9816                    partitions: Vec::new(),
9817                    identifier_func: None,
9818                    changes: None,
9819                    version: None,
9820                    span: None,
9821                }
9822            };
9823            Some(Expression::Table(Box::new(source_table)))
9824        } else {
9825            None
9826        };
9827
9828        // Parse optional AS alias after VALUES (MySQL: INSERT ... VALUES (...) AS new_data)
9829        let source_alias = if self.match_token(TokenType::As) {
9830            Some(Identifier::new(self.expect_identifier()?))
9831        } else {
9832            None
9833        };
9834
9835        // Parse ON CONFLICT clause (PostgreSQL, SQLite) or ON DUPLICATE KEY UPDATE (MySQL)
9836        let on_conflict = if self.match_token(TokenType::On) {
9837            if self.match_identifier("CONFLICT") {
9838                Some(Box::new(self.parse_on_conflict()?))
9839            } else if self.match_identifier("DUPLICATE") {
9840                // MySQL: ON DUPLICATE KEY UPDATE
9841                self.expect(TokenType::Key)?;
9842                self.expect(TokenType::Update)?;
9843
9844                // Parse the UPDATE SET expressions
9845                let mut sets = Vec::new();
9846                loop {
9847                    // Parse column = expression
9848                    let col_name = self.expect_identifier_with_quoted()?;
9849                    // Handle qualified column: table.column
9850                    let column = if self.match_token(TokenType::Dot) {
9851                        let col = self.expect_identifier_with_quoted()?;
9852                        Expression::boxed_column(Column {
9853                            name: col,
9854                            table: Some(col_name),
9855                            join_mark: false,
9856                            trailing_comments: Vec::new(),
9857                            span: None,
9858                            inferred_type: None,
9859                        })
9860                    } else {
9861                        Expression::Identifier(col_name)
9862                    };
9863                    self.expect(TokenType::Eq)?;
9864                    let value = self.parse_expression()?;
9865                    sets.push(Expression::Eq(Box::new(BinaryOp {
9866                        left: column,
9867                        right: value,
9868                        left_comments: Vec::new(),
9869                        operator_comments: Vec::new(),
9870                        trailing_comments: Vec::new(),
9871                        inferred_type: None,
9872                    })));
9873                    if !self.match_token(TokenType::Comma) {
9874                        break;
9875                    }
9876                }
9877
9878                Some(Box::new(Expression::OnConflict(Box::new(OnConflict {
9879                    duplicate: Some(Box::new(Expression::Boolean(BooleanLiteral {
9880                        value: true,
9881                    }))),
9882                    expressions: sets,
9883                    action: None,
9884                    conflict_keys: None,
9885                    index_predicate: None,
9886                    constraint: None,
9887                    where_: None,
9888                }))))
9889            } else {
9890                // Unexpected token after ON
9891                return Err(self.parse_error("Expected CONFLICT or DUPLICATE after ON"));
9892            }
9893        } else {
9894            None
9895        };
9896
9897        // Parse RETURNING clause (PostgreSQL, SQLite)
9898        let returning = if self.match_token(TokenType::Returning) {
9899            self.parse_select_expressions()?
9900        } else {
9901            Vec::new()
9902        };
9903
9904        Ok(Expression::Insert(Box::new(Insert {
9905            table,
9906            columns,
9907            values,
9908            query,
9909            overwrite,
9910            partition,
9911            directory: None,
9912            returning,
9913            output,
9914            on_conflict,
9915            leading_comments,
9916            if_exists,
9917            with: None,
9918            ignore,
9919            source_alias,
9920            alias,
9921            alias_explicit_as,
9922            default_values,
9923            by_name,
9924            conflict_action,
9925            is_replace: false,
9926            replace_where,
9927            source: source.map(Box::new),
9928            hint,
9929            function_target,
9930            partition_by: partition_by_expr,
9931            settings: insert_settings,
9932        })))
9933    }
9934
9935    /// Parse ON CONFLICT clause for INSERT statements (PostgreSQL, SQLite)
9936    /// Syntax: ON CONFLICT [(conflict_target)] [WHERE predicate] DO NOTHING | DO UPDATE SET ...
9937    /// ON CONFLICT ON CONSTRAINT constraint_name DO ...
9938    fn parse_on_conflict(&mut self) -> Result<Expression> {
9939        // Check for ON CONSTRAINT variant
9940        let constraint =
9941            if self.match_token(TokenType::On) && self.match_token(TokenType::Constraint) {
9942                let name = self.expect_identifier()?;
9943                Some(Box::new(Expression::Identifier(Identifier::new(name))))
9944            } else {
9945                None
9946            };
9947
9948        // Parse optional conflict target (column list)
9949        let conflict_keys = if constraint.is_none() && self.match_token(TokenType::LParen) {
9950            let keys = self.parse_expression_list()?;
9951            self.expect(TokenType::RParen)?;
9952            Some(Box::new(Expression::Tuple(Box::new(Tuple {
9953                expressions: keys,
9954            }))))
9955        } else {
9956            None
9957        };
9958
9959        // Parse optional WHERE clause for conflict target
9960        let index_predicate = if self.match_token(TokenType::Where) {
9961            Some(Box::new(self.parse_expression()?))
9962        } else {
9963            None
9964        };
9965
9966        // Parse DO NOTHING or DO UPDATE
9967        if !self.match_identifier("DO") {
9968            return Err(self.parse_error("Expected DO after ON CONFLICT"));
9969        }
9970
9971        let action = if self.match_identifier("NOTHING") {
9972            // DO NOTHING
9973            Some(Box::new(Expression::Identifier(Identifier::new(
9974                "NOTHING".to_string(),
9975            ))))
9976        } else if self.match_token(TokenType::Update) {
9977            // DO UPDATE SET ...
9978            self.expect(TokenType::Set)?;
9979            let mut sets = Vec::new();
9980            loop {
9981                // Parse column = expression
9982                let col_name = self.expect_identifier_with_quoted()?;
9983                // Handle qualified column: table.column
9984                let column = if self.match_token(TokenType::Dot) {
9985                    let col = self.expect_identifier_with_quoted()?;
9986                    Expression::boxed_column(Column {
9987                        name: col,
9988                        table: Some(col_name),
9989                        join_mark: false,
9990                        trailing_comments: Vec::new(),
9991                        span: None,
9992                        inferred_type: None,
9993                    })
9994                } else {
9995                    Expression::Identifier(col_name)
9996                };
9997                self.expect(TokenType::Eq)?;
9998                let value = self.parse_expression()?;
9999                sets.push(Expression::Eq(Box::new(BinaryOp {
10000                    left: column,
10001                    right: value,
10002                    left_comments: Vec::new(),
10003                    operator_comments: Vec::new(),
10004                    trailing_comments: Vec::new(),
10005                    inferred_type: None,
10006                })));
10007                if !self.match_token(TokenType::Comma) {
10008                    break;
10009                }
10010            }
10011            Some(Box::new(Expression::Tuple(Box::new(Tuple {
10012                expressions: sets,
10013            }))))
10014        } else {
10015            return Err(self.parse_error("Expected NOTHING or UPDATE after DO"));
10016        };
10017
10018        // Parse optional WHERE clause for the UPDATE action
10019        let where_ = if self.match_token(TokenType::Where) {
10020            Some(Box::new(self.parse_expression()?))
10021        } else {
10022            None
10023        };
10024
10025        Ok(Expression::OnConflict(Box::new(OnConflict {
10026            duplicate: None,
10027            expressions: Vec::new(),
10028            action,
10029            conflict_keys,
10030            index_predicate,
10031            constraint,
10032            where_,
10033        })))
10034    }
10035
10036    /// Parse MySQL REPLACE [INTO] statement or REPLACE() function call
10037    fn parse_replace(&mut self) -> Result<Expression> {
10038        // Check if this is REPLACE() function call (REPLACE followed by '(')
10039        // or MySQL REPLACE INTO statement
10040        let replace_token = self.expect(TokenType::Replace)?;
10041        let leading_comments = replace_token.comments;
10042
10043        if self.check(TokenType::LParen) {
10044            // This is a REPLACE() function call, parse as expression
10045            self.expect(TokenType::LParen)?;
10046            let args = self.parse_expression_list()?;
10047            self.expect(TokenType::RParen)?;
10048            return Ok(Expression::Function(Box::new(Function {
10049                name: "REPLACE".to_string(),
10050                args,
10051                distinct: false,
10052                trailing_comments: Vec::new(),
10053                use_bracket_syntax: false,
10054                no_parens: false,
10055                quoted: false,
10056                span: None,
10057                inferred_type: None,
10058            })));
10059        }
10060
10061        // Teradata: REPLACE VIEW -> CREATE OR REPLACE VIEW
10062        if matches!(
10063            self.config.dialect,
10064            Some(crate::dialects::DialectType::Teradata)
10065        ) && self.check(TokenType::View)
10066        {
10067            return self.parse_create_view(true, false, false, false, None, None, None, false);
10068        }
10069
10070        // ClickHouse: REPLACE TABLE -> treat like CREATE OR REPLACE TABLE
10071        // Also handle REPLACE TEMPORARY TABLE
10072        if matches!(
10073            self.config.dialect,
10074            Some(crate::dialects::DialectType::ClickHouse)
10075        ) && (self.check(TokenType::Table) || self.check(TokenType::Temporary))
10076        {
10077            let temporary = self.match_token(TokenType::Temporary);
10078            return self.parse_create_table(true, temporary, leading_comments.clone(), None);
10079        }
10080
10081        // ClickHouse: REPLACE DICTIONARY -> consume as Command
10082        if matches!(
10083            self.config.dialect,
10084            Some(crate::dialects::DialectType::ClickHouse)
10085        ) && (self.check(TokenType::Dictionary) || self.check_identifier("DICTIONARY"))
10086        {
10087            let mut parts = vec!["REPLACE".to_string()];
10088            let mut _paren_depth = 0i32;
10089            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
10090                let token = self.advance();
10091                if token.token_type == TokenType::LParen {
10092                    _paren_depth += 1;
10093                }
10094                if token.token_type == TokenType::RParen {
10095                    _paren_depth -= 1;
10096                }
10097                let text = if token.token_type == TokenType::String {
10098                    format!("'{}'", token.text)
10099                } else if token.token_type == TokenType::QuotedIdentifier {
10100                    format!("\"{}\"", token.text)
10101                } else {
10102                    token.text.clone()
10103                };
10104                parts.push(text);
10105            }
10106            return Ok(Expression::Command(Box::new(crate::expressions::Command {
10107                this: parts.join(" "),
10108            })));
10109        }
10110
10111        // Otherwise, this is MySQL/SQLite REPLACE INTO statement - parse similarly to INSERT
10112        self.match_token(TokenType::Into);
10113
10114        let table_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
10115        let table = if self.match_token(TokenType::Dot) {
10116            let second_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
10117            TableRef {
10118                name: second_name,
10119                schema: Some(table_name),
10120                catalog: None,
10121                alias: None,
10122                alias_explicit_as: false,
10123                column_aliases: Vec::new(),
10124                leading_comments: Vec::new(),
10125                trailing_comments: Vec::new(),
10126                when: None,
10127                only: false,
10128                final_: false,
10129                table_sample: None,
10130                hints: Vec::new(),
10131                system_time: None,
10132                partitions: Vec::new(),
10133                identifier_func: None,
10134                changes: None,
10135                version: None,
10136                span: None,
10137            }
10138        } else {
10139            TableRef::new(table_name.name)
10140        };
10141
10142        // Parse optional column list
10143        let columns = if self.match_token(TokenType::LParen) {
10144            let mut cols = Vec::new();
10145            loop {
10146                if self.check(TokenType::RParen) {
10147                    break;
10148                }
10149                let col = self.expect_identifier_with_quoted()?;
10150                cols.push(col);
10151                if !self.match_token(TokenType::Comma) {
10152                    break;
10153                }
10154            }
10155            self.expect(TokenType::RParen)?;
10156            cols
10157        } else {
10158            Vec::new()
10159        };
10160
10161        // Parse VALUES or SELECT query
10162        let mut values = Vec::new();
10163        let query = if self.match_token(TokenType::Values) {
10164            loop {
10165                self.expect(TokenType::LParen)?;
10166                let row = self.parse_expression_list()?;
10167                self.expect(TokenType::RParen)?;
10168                values.push(row);
10169                if !self.match_token(TokenType::Comma) {
10170                    break;
10171                }
10172            }
10173            None
10174        } else if !self.is_at_end() && !self.check(TokenType::Semicolon) {
10175            // SELECT or other statement as value source
10176            Some(self.parse_statement()?)
10177        } else {
10178            None
10179        };
10180
10181        Ok(Expression::Insert(Box::new(Insert {
10182            table,
10183            columns,
10184            values,
10185            query,
10186            overwrite: false,
10187            partition: Vec::new(),
10188            directory: None,
10189            returning: Vec::new(),
10190            output: None,
10191            on_conflict: None,
10192            leading_comments,
10193            if_exists: false,
10194            with: None,
10195            ignore: false,
10196            source_alias: None,
10197            alias: None,
10198            alias_explicit_as: false,
10199            default_values: false,
10200            by_name: false,
10201            conflict_action: None,
10202            is_replace: true,
10203            replace_where: None,
10204            source: None,
10205            hint: None,
10206            function_target: None,
10207            partition_by: None,
10208            settings: Vec::new(),
10209        })))
10210    }
10211
10212    /// Parse UPDATE statement
10213    fn parse_update(&mut self) -> Result<Expression> {
10214        let update_token = self.expect(TokenType::Update)?;
10215        let leading_comments = update_token.comments;
10216
10217        // TSQL: UPDATE STATISTICS table_name - parse as Command
10218        if self.check_identifier("STATISTICS") {
10219            let mut parts = vec!["UPDATE".to_string()];
10220            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
10221                parts.push(self.advance().text);
10222            }
10223            return Ok(Expression::Command(Box::new(Command {
10224                this: parts.join(" "),
10225            })));
10226        }
10227
10228        // PostgreSQL ONLY modifier: UPDATE ONLY t SET ...
10229        let has_only = self.match_token(TokenType::Only);
10230
10231        // Parse table name (can be qualified: db.table_name)
10232        let first_name = self.expect_identifier_with_quoted()?;
10233        let mut table = if self.match_token(TokenType::Dot) {
10234            let second_name = self.expect_identifier_with_quoted()?;
10235            // Check for three-part name (catalog.schema.table)
10236            if self.match_token(TokenType::Dot) {
10237                let table_name = self.expect_identifier_with_quoted()?;
10238                TableRef {
10239                    name: table_name,
10240                    schema: Some(second_name),
10241                    catalog: Some(first_name),
10242                    alias: None,
10243                    alias_explicit_as: false,
10244                    column_aliases: Vec::new(),
10245                    leading_comments: Vec::new(),
10246                    trailing_comments: Vec::new(),
10247                    when: None,
10248                    only: false,
10249                    final_: false,
10250                    table_sample: None,
10251                    hints: Vec::new(),
10252                    system_time: None,
10253                    partitions: Vec::new(),
10254                    identifier_func: None,
10255                    changes: None,
10256                    version: None,
10257                    span: None,
10258                }
10259            } else {
10260                TableRef {
10261                    name: second_name,
10262                    schema: Some(first_name),
10263                    catalog: None,
10264                    alias: None,
10265                    alias_explicit_as: false,
10266                    column_aliases: Vec::new(),
10267                    leading_comments: Vec::new(),
10268                    trailing_comments: Vec::new(),
10269                    when: None,
10270                    only: false,
10271                    final_: false,
10272                    table_sample: None,
10273                    hints: Vec::new(),
10274                    system_time: None,
10275                    partitions: Vec::new(),
10276                    identifier_func: None,
10277                    changes: None,
10278                    version: None,
10279                    span: None,
10280                }
10281            }
10282        } else {
10283            TableRef::from_identifier(first_name)
10284        };
10285        table.trailing_comments = self.previous_trailing_comments().to_vec();
10286        if has_only {
10287            table.only = true;
10288        }
10289
10290        // Optional alias (with or without AS)
10291        if self.match_token(TokenType::As) {
10292            table.alias = Some(self.expect_identifier_with_quoted()?);
10293            table.alias_explicit_as = true;
10294        } else if self.is_identifier_token() && !self.check(TokenType::Set) {
10295            // Implicit alias (table t SET ...)
10296            table.alias = Some(self.expect_identifier_with_quoted()?);
10297            table.alias_explicit_as = false;
10298        }
10299
10300        // Handle multi-table UPDATE syntax: UPDATE t1, t2, t3 LEFT JOIN t4 ON ... SET ...
10301        // Capture additional tables
10302        let mut extra_tables = Vec::new();
10303        while self.match_token(TokenType::Comma) {
10304            // Parse additional table name
10305            let first_name = self.expect_identifier_with_quoted()?;
10306            let mut extra_table = if self.match_token(TokenType::Dot) {
10307                let second_name = self.expect_identifier_with_quoted()?;
10308                if self.match_token(TokenType::Dot) {
10309                    let table_name = self.expect_identifier_with_quoted()?;
10310                    TableRef {
10311                        name: table_name,
10312                        schema: Some(second_name),
10313                        catalog: Some(first_name),
10314                        alias: None,
10315                        alias_explicit_as: false,
10316                        column_aliases: Vec::new(),
10317                        leading_comments: Vec::new(),
10318                        trailing_comments: Vec::new(),
10319                        when: None,
10320                        only: false,
10321                        final_: false,
10322                        table_sample: None,
10323                        hints: Vec::new(),
10324                        system_time: None,
10325                        partitions: Vec::new(),
10326                        identifier_func: None,
10327                        changes: None,
10328                        version: None,
10329                        span: None,
10330                    }
10331                } else {
10332                    TableRef {
10333                        name: second_name,
10334                        schema: Some(first_name),
10335                        catalog: None,
10336                        alias: None,
10337                        alias_explicit_as: false,
10338                        column_aliases: Vec::new(),
10339                        leading_comments: Vec::new(),
10340                        trailing_comments: Vec::new(),
10341                        when: None,
10342                        only: false,
10343                        final_: false,
10344                        table_sample: None,
10345                        hints: Vec::new(),
10346                        system_time: None,
10347                        partitions: Vec::new(),
10348                        identifier_func: None,
10349                        changes: None,
10350                        version: None,
10351                        span: None,
10352                    }
10353                }
10354            } else {
10355                TableRef::from_identifier(first_name)
10356            };
10357            // Optional alias
10358            if self.match_token(TokenType::As) {
10359                extra_table.alias = Some(self.expect_identifier_with_quoted()?);
10360                extra_table.alias_explicit_as = true;
10361            } else if self.is_identifier_token()
10362                && !self.check(TokenType::Set)
10363                && !self.check_keyword()
10364            {
10365                extra_table.alias = Some(self.expect_identifier_with_quoted()?);
10366                extra_table.alias_explicit_as = false;
10367            }
10368            extra_tables.push(extra_table);
10369        }
10370
10371        // Handle JOINs before SET
10372        let mut table_joins = Vec::new();
10373        while let Some((kind, _, use_inner_keyword, use_outer_keyword, _join_hint)) =
10374            self.try_parse_join_kind()
10375        {
10376            if self.check(TokenType::Join) {
10377                self.skip(); // consume JOIN
10378            }
10379            // Parse joined table (supports subqueries, LATERAL, functions, etc.)
10380            let join_expr = self.parse_table_expression()?;
10381            // ON clause
10382            let on_condition = if self.match_token(TokenType::On) {
10383                Some(self.parse_expression()?)
10384            } else {
10385                None
10386            };
10387            table_joins.push(Join {
10388                this: join_expr,
10389                on: on_condition,
10390                using: Vec::new(),
10391                kind,
10392                use_inner_keyword,
10393                use_outer_keyword,
10394                deferred_condition: false,
10395                join_hint: None,
10396                match_condition: None,
10397                pivots: Vec::new(),
10398                comments: Vec::new(),
10399                nesting_group: 0,
10400                directed: false,
10401            });
10402        }
10403
10404        // Snowflake syntax: UPDATE table FROM (source) SET ... WHERE ...
10405        // Check if FROM comes before SET
10406        let (from_before_set, early_from_clause, early_from_joins) =
10407            if self.match_token(TokenType::From) {
10408                let from_clause = self.parse_from()?;
10409                let from_joins = self.parse_joins()?;
10410                (true, Some(from_clause), from_joins)
10411            } else {
10412                (false, None, Vec::new())
10413            };
10414
10415        self.expect(TokenType::Set)?;
10416
10417        let mut set = Vec::new();
10418        loop {
10419            // Column can be qualified for multi-table UPDATE (e.g., a.id = 1)
10420            // Use safe keyword variant to allow keywords like 'exists' as column names (ClickHouse)
10421            let mut col_ident = self.expect_identifier_or_safe_keyword_with_quoted()?;
10422            while self.match_token(TokenType::Dot) {
10423                let part = self.expect_identifier_or_safe_keyword_with_quoted()?;
10424                // For qualified columns, preserve both parts
10425                col_ident = Identifier {
10426                    name: format!("{}.{}", col_ident.name, part.name),
10427                    quoted: col_ident.quoted || part.quoted,
10428                    trailing_comments: Vec::new(),
10429                    span: None,
10430                };
10431            }
10432            self.expect(TokenType::Eq)?;
10433            let value = self.parse_expression()?;
10434            set.push((col_ident, value));
10435
10436            if !self.match_token(TokenType::Comma) {
10437                break;
10438            }
10439        }
10440
10441        // Parse OUTPUT clause (TSQL)
10442        let output = if self.match_token(TokenType::Output) {
10443            Some(self.parse_output_clause()?)
10444        } else {
10445            None
10446        };
10447
10448        // Parse FROM clause (PostgreSQL, SQL Server, Snowflake) - only if not already parsed before SET
10449        let (from_clause, from_joins) = if from_before_set {
10450            (early_from_clause, early_from_joins)
10451        } else if self.match_token(TokenType::From) {
10452            let from_clause = Some(self.parse_from()?);
10453            let from_joins = self.parse_joins()?;
10454            (from_clause, from_joins)
10455        } else {
10456            (None, Vec::new())
10457        };
10458
10459        let where_clause = if self.match_token(TokenType::Where) {
10460            Some(Where {
10461                this: self.parse_expression()?,
10462            })
10463        } else {
10464            None
10465        };
10466
10467        // Parse RETURNING clause (PostgreSQL, SQLite)
10468        let returning = if self.match_token(TokenType::Returning) {
10469            self.parse_select_expressions()?
10470        } else {
10471            Vec::new()
10472        };
10473
10474        // Parse ORDER BY clause (MySQL)
10475        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
10476            Some(self.parse_order_by()?)
10477        } else {
10478            None
10479        };
10480
10481        // Parse LIMIT clause (MySQL)
10482        let limit = if self.match_token(TokenType::Limit) {
10483            Some(self.parse_expression()?)
10484        } else {
10485            None
10486        };
10487
10488        Ok(Expression::Update(Box::new(Update {
10489            table,
10490            extra_tables,
10491            table_joins,
10492            set,
10493            from_clause,
10494            from_joins,
10495            where_clause,
10496            returning,
10497            output,
10498            with: None,
10499            leading_comments,
10500            limit,
10501            order_by,
10502            from_before_set,
10503        })))
10504    }
10505
10506    /// Parse DELETE statement
10507    /// Handles:
10508    /// - Standard: DELETE FROM t WHERE ...
10509    /// - PostgreSQL USING: DELETE FROM t USING s WHERE ... RETURNING a
10510    /// - DuckDB USING: DELETE FROM t USING (VALUES ...) AS t1 WHERE ...
10511    /// - MySQL multi-table: DELETE t1 FROM t1 JOIN t2 ON ... WHERE ...
10512    /// - MySQL multi-table: DELETE t1, t2 FROM t1 JOIN t2 JOIN t3 WHERE ...
10513    /// - MySQL USING: DELETE FROM t1, t2 USING t1 JOIN t2 JOIN t3 WHERE ...
10514    /// - MySQL FORCE INDEX: DELETE FROM t FORCE INDEX (idx) WHERE ...
10515    fn parse_delete(&mut self) -> Result<Expression> {
10516        let delete_token = self.expect(TokenType::Delete)?;
10517        let leading_comments = delete_token.comments;
10518
10519        // Check if FROM is present. If not, this is MySQL multi-table: DELETE t1, t2 FROM ...
10520        // or TSQL: DELETE x OUTPUT x.a FROM z
10521        let mut tables = Vec::new();
10522        let mut early_output = None;
10523        let _has_from = if self.check(TokenType::From) {
10524            self.skip(); // consume FROM
10525            true
10526        } else {
10527            // MySQL multi-table: DELETE t1[, t2, ...] FROM ...
10528            // or TSQL: DELETE x OUTPUT x.a FROM z
10529            // or BigQuery/generic: DELETE table WHERE ... (no FROM required)
10530            // Parse target table list (supporting dotted names)
10531            loop {
10532                let tref = self.parse_table_ref()?;
10533                tables.push(tref);
10534                if !self.match_token(TokenType::Comma) {
10535                    break;
10536                }
10537            }
10538            // TSQL: OUTPUT clause can appear before FROM
10539            if self.match_token(TokenType::Output) {
10540                early_output = Some(self.parse_output_clause()?);
10541            }
10542            if self.check(TokenType::From) {
10543                self.skip(); // consume FROM
10544                true
10545            } else {
10546                // BigQuery-style: DELETE table WHERE ... (no FROM)
10547                false
10548            }
10549        };
10550
10551        // Now parse the main table after FROM (or use from no-FROM path)
10552        let has_only = self.match_token(TokenType::Only);
10553        let mut table = if _has_from {
10554            // Parse the main table(s) after FROM
10555            // Use parse_table_ref() to handle dotted names like db.table
10556            self.parse_table_ref()?
10557        } else {
10558            // BigQuery-style: table was already parsed into `tables`
10559            // Move it out to be the main table
10560            if !tables.is_empty() {
10561                tables.remove(0)
10562            } else {
10563                return Err(self.parse_error("Expected table name in DELETE statement"));
10564            }
10565        };
10566        if has_only {
10567            table.only = true;
10568        }
10569
10570        // ClickHouse: ON CLUSTER clause
10571        let on_cluster = self.parse_on_cluster_clause()?;
10572
10573        // Check for additional tables after the first: DELETE FROM t1, t2 USING ...
10574        let mut extra_from_tables = Vec::new();
10575        if _has_from
10576            && tables.is_empty()
10577            && self.check(TokenType::Comma)
10578            && !self.check(TokenType::Where)
10579        {
10580            // Could be multi-table: DELETE FROM t1, t2 USING ...
10581            // Check ahead if this is followed by USING or more tables
10582            while self.match_token(TokenType::Comma) {
10583                let extra_name = self.expect_identifier_with_quoted()?;
10584                let extra_ref = TableRef::from_identifier(extra_name);
10585                extra_from_tables.push(extra_ref);
10586            }
10587        }
10588
10589        // If we had DELETE FROM t1, t2 USING ..., the tables field stores t1, t2
10590        let mut tables_from_using = false;
10591        if !extra_from_tables.is_empty() {
10592            // The main table + extra tables form the multi-table target
10593            tables.push(table.clone());
10594            tables.append(&mut extra_from_tables);
10595            tables_from_using = true;
10596        }
10597
10598        // Check for FORCE INDEX hint (MySQL): DELETE FROM t FORCE INDEX (idx)
10599        let force_index = if self.match_text_seq(&["FORCE", "INDEX"]) {
10600            self.expect(TokenType::LParen)?;
10601            let idx_name = self.expect_identifier_with_quoted()?;
10602            self.expect(TokenType::RParen)?;
10603            Some(idx_name.name)
10604        } else {
10605            None
10606        };
10607
10608        // Check for optional alias (with or without AS)
10609        let (alias, alias_explicit_as) = if force_index.is_none() && self.match_token(TokenType::As)
10610        {
10611            (Some(self.expect_identifier_with_quoted()?), true)
10612        } else if force_index.is_none()
10613            && self.is_identifier_token()
10614            && !self.check(TokenType::Using)
10615            && !self.check(TokenType::Where)
10616            && !self.check(TokenType::Inner)
10617            && !self.check(TokenType::Left)
10618            && !self.check(TokenType::Right)
10619            && !self.check(TokenType::Cross)
10620            && !self.check(TokenType::Full)
10621            && !self.check(TokenType::Join)
10622            && !self.check_identifier("FORCE")
10623        {
10624            (Some(self.expect_identifier_with_quoted()?), false)
10625        } else {
10626            (None, false)
10627        };
10628
10629        // Parse JOINs for MySQL multi-table: DELETE t1 FROM t1 LEFT JOIN t2 ON ...
10630        let mut joins = self.parse_joins()?;
10631
10632        // Parse USING clause (PostgreSQL/DuckDB/MySQL)
10633        let mut using = Vec::new();
10634        if self.match_token(TokenType::Using) {
10635            loop {
10636                // Check for subquery: USING (SELECT ...) AS ... or (VALUES ...) AS ...
10637                if self.check(TokenType::LParen) {
10638                    // Check if next token after ( is VALUES
10639                    let is_values = self.current + 1 < self.tokens.len()
10640                        && self.tokens[self.current + 1].token_type == TokenType::Values;
10641                    let subquery = if is_values {
10642                        // Parse (VALUES ...) as parenthesized VALUES
10643                        self.skip(); // consume (
10644                        let values = self.parse_values()?;
10645                        self.expect(TokenType::RParen)?;
10646                        Expression::Paren(Box::new(Paren {
10647                            this: values,
10648                            trailing_comments: Vec::new(),
10649                        }))
10650                    } else {
10651                        // Parse as subquery (SELECT ...) or other expression
10652                        self.parse_primary()?
10653                    };
10654                    // Parse alias
10655                    let using_alias = if self.match_token(TokenType::As) {
10656                        let alias_name = self.expect_identifier_with_quoted()?;
10657                        // Check for column aliases: AS name(col1, col2)
10658                        let col_aliases = if self.match_token(TokenType::LParen) {
10659                            let aliases = self.parse_identifier_list()?;
10660                            self.expect(TokenType::RParen)?;
10661                            aliases
10662                        } else {
10663                            Vec::new()
10664                        };
10665                        Some((alias_name, col_aliases))
10666                    } else {
10667                        None
10668                    };
10669                    // Create a TableRef from the subquery with alias
10670                    let mut tref = TableRef::new("");
10671                    if let Some((alias_name, col_aliases)) = using_alias {
10672                        tref.alias = Some(alias_name);
10673                        tref.alias_explicit_as = true;
10674                        tref.column_aliases = col_aliases;
10675                    }
10676                    // Store the subquery in the table reference using hints (as a hack)
10677                    // Actually, we need a better approach - use the table ref hints to store the subquery
10678                    tref.hints = vec![subquery];
10679                    using.push(tref);
10680                } else {
10681                    let using_table = self.expect_identifier_with_quoted()?;
10682                    let mut using_ref = TableRef::from_identifier(using_table);
10683
10684                    // Check for JOINs: USING t1 INNER JOIN t2 INNER JOIN t3
10685                    if self.check_join_keyword() {
10686                        // Parse JOINs as part of USING
10687                        using.push(using_ref);
10688                        let mut using_joins = self.parse_joins()?;
10689                        joins.append(&mut using_joins);
10690                        break;
10691                    }
10692
10693                    // Optional alias for using table
10694                    if self.match_token(TokenType::As) {
10695                        using_ref.alias = Some(self.expect_identifier_with_quoted()?);
10696                        using_ref.alias_explicit_as = true;
10697                    } else if self.is_identifier_token()
10698                        && !self.check(TokenType::Comma)
10699                        && !self.check(TokenType::Where)
10700                    {
10701                        using_ref.alias = Some(self.expect_identifier_with_quoted()?);
10702                    }
10703                    using.push(using_ref);
10704                }
10705                if !self.match_token(TokenType::Comma) {
10706                    break;
10707                }
10708            }
10709        }
10710
10711        // ClickHouse: IN PARTITION 'partition_id' clause before WHERE
10712        if matches!(
10713            self.config.dialect,
10714            Some(crate::dialects::DialectType::ClickHouse)
10715        ) && self.check(TokenType::In)
10716            && self
10717                .peek_nth(1)
10718                .is_some_and(|t| t.text.eq_ignore_ascii_case("PARTITION"))
10719        {
10720            self.skip(); // consume IN
10721            self.skip(); // consume PARTITION
10722                         // Consume partition expression (string or identifier)
10723            let _partition = self.parse_primary()?;
10724        }
10725
10726        // Parse OUTPUT clause (TSQL) - may have been parsed early (before FROM)
10727        let output = if early_output.is_some() {
10728            early_output
10729        } else if self.match_token(TokenType::Output) {
10730            Some(self.parse_output_clause()?)
10731        } else {
10732            None
10733        };
10734
10735        let where_clause = if self.match_token(TokenType::Where) {
10736            Some(Where {
10737                this: self.parse_expression()?,
10738            })
10739        } else {
10740            None
10741        };
10742
10743        // Parse ORDER BY clause (MySQL)
10744        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
10745            Some(self.parse_order_by()?)
10746        } else {
10747            None
10748        };
10749
10750        // Parse LIMIT clause (MySQL)
10751        let limit = if self.match_token(TokenType::Limit) {
10752            Some(self.parse_expression()?)
10753        } else {
10754            None
10755        };
10756
10757        // Parse RETURNING clause (PostgreSQL)
10758        let returning = if self.match_token(TokenType::Returning) {
10759            self.parse_select_expressions()?
10760        } else {
10761            Vec::new()
10762        };
10763
10764        Ok(Expression::Delete(Box::new(Delete {
10765            table,
10766            on_cluster,
10767            alias,
10768            alias_explicit_as,
10769            using,
10770            where_clause,
10771            output,
10772            leading_comments,
10773            with: None,
10774            limit,
10775            order_by,
10776            returning,
10777            tables,
10778            tables_from_using,
10779            joins,
10780            force_index,
10781            no_from: !_has_from,
10782        })))
10783    }
10784
10785    // ==================== DDL Parsing ====================
10786
10787    /// Parse a CREATE statement
10788    fn parse_create(&mut self) -> Result<Expression> {
10789        let create_pos = self.current; // position of CREATE token
10790        let create_token = self.expect(TokenType::Create)?;
10791        let leading_comments = create_token.comments;
10792
10793        // Handle OR REPLACE / OR ALTER (TSQL)
10794        let or_replace = self.match_keywords(&[TokenType::Or, TokenType::Replace]);
10795        let or_alter = !or_replace && self.match_text_seq(&["OR", "ALTER"]);
10796
10797        // Handle TEMPORARY
10798        let temporary = self.match_token(TokenType::Temporary);
10799
10800        // Handle MATERIALIZED
10801        let materialized = self.match_token(TokenType::Materialized);
10802
10803        // Parse MySQL-specific CREATE VIEW options: ALGORITHM, DEFINER, SQL SECURITY
10804        // CREATE ALGORITHM=... DEFINER=... SQL SECURITY DEFINER VIEW ...
10805        let mut algorithm: Option<String> = None;
10806        let mut definer: Option<String> = None;
10807        let mut security: Option<FunctionSecurity> = None;
10808
10809        while self.match_identifier("ALGORITHM")
10810            || self.match_identifier("DEFINER")
10811            || self.match_identifier("SQL")
10812        {
10813            let option_name = self.previous().text.to_ascii_uppercase();
10814
10815            if option_name == "ALGORITHM" && self.match_token(TokenType::Eq) {
10816                // ALGORITHM=UNDEFINED|MERGE|TEMPTABLE
10817                let value = self.expect_identifier_or_keyword()?;
10818                algorithm = Some(value.to_ascii_uppercase());
10819            } else if option_name == "DEFINER" && self.match_token(TokenType::Eq) {
10820                // DEFINER=user@host (can include @ and %)
10821                let mut definer_value = String::new();
10822                while !self.is_at_end()
10823                    && !self.check(TokenType::View)
10824                    && !self.check_identifier("ALGORITHM")
10825                    && !self.check_identifier("DEFINER")
10826                    && !self.check_identifier("SQL")
10827                    && !self.check_identifier("SECURITY")
10828                {
10829                    definer_value.push_str(&self.advance().text);
10830                }
10831                definer = Some(definer_value);
10832            } else if option_name == "SQL" && self.match_identifier("SECURITY") {
10833                // SQL SECURITY DEFINER/INVOKER
10834                if self.match_identifier("DEFINER") {
10835                    security = Some(FunctionSecurity::Definer);
10836                } else if self.match_identifier("INVOKER") {
10837                    security = Some(FunctionSecurity::Invoker);
10838                }
10839            }
10840        }
10841
10842        // Handle SECURE modifier for VIEW (Snowflake)
10843        let secure = self.match_identifier("SECURE");
10844
10845        // Handle table modifiers: DYNAMIC, ICEBERG, EXTERNAL, HYBRID, TRANSIENT (Snowflake), UNLOGGED (PostgreSQL)
10846        let mut table_modifier: Option<String> = if self.check_identifier("DYNAMIC") {
10847            self.skip();
10848            Some("DYNAMIC".to_string())
10849        } else if self.check_identifier("ICEBERG") {
10850            self.skip();
10851            Some("ICEBERG".to_string())
10852        } else if self.check_identifier("EXTERNAL") {
10853            self.skip();
10854            Some("EXTERNAL".to_string())
10855        } else if self.check_identifier("HYBRID") {
10856            self.skip();
10857            Some("HYBRID".to_string())
10858        } else if self.check_identifier("TRANSIENT") {
10859            self.skip();
10860            Some("TRANSIENT".to_string())
10861        } else if self.check_identifier("UNLOGGED") {
10862            self.skip();
10863            Some("UNLOGGED".to_string())
10864        } else if self.check_identifier("DICTIONARY") {
10865            self.skip();
10866            Some("DICTIONARY".to_string())
10867        } else if self.check(TokenType::Dictionary) {
10868            self.skip();
10869            Some("DICTIONARY".to_string())
10870        } else {
10871            None
10872        };
10873
10874        // Teradata: SET/MULTISET/VOLATILE/GLOBAL TEMPORARY modifiers before TABLE
10875        if matches!(
10876            self.config.dialect,
10877            Some(crate::dialects::DialectType::Teradata)
10878        ) {
10879            let mut parts = Vec::new();
10880            loop {
10881                if self.match_token(TokenType::Set) {
10882                    parts.push(self.previous().text.to_ascii_uppercase());
10883                } else if self.match_identifier("MULTISET") {
10884                    parts.push(self.previous().text.to_ascii_uppercase());
10885                } else if self.match_identifier("VOLATILE") {
10886                    parts.push(self.previous().text.to_ascii_uppercase());
10887                } else if self.match_identifier("GLOBAL") {
10888                    parts.push(self.previous().text.to_ascii_uppercase());
10889                } else if self.match_token(TokenType::Temporary) {
10890                    parts.push(self.previous().text.to_ascii_uppercase());
10891                } else {
10892                    break;
10893                }
10894            }
10895            if !parts.is_empty() {
10896                table_modifier = Some(parts.join(" "));
10897            }
10898        }
10899
10900        if table_modifier.as_deref() == Some("DICTIONARY") {
10901            return self.parse_create_table(
10902                or_replace,
10903                temporary,
10904                leading_comments,
10905                table_modifier.as_deref(),
10906            );
10907        }
10908
10909        match self.peek().token_type {
10910            TokenType::Table => {
10911                // Check if this is CREATE TABLE FUNCTION (BigQuery)
10912                if self.current + 1 < self.tokens.len()
10913                    && self.tokens[self.current + 1].token_type == TokenType::Function
10914                {
10915                    self.skip(); // consume TABLE
10916                    return self.parse_create_function(or_replace, or_alter, temporary, true);
10917                }
10918                let modifier = if materialized {
10919                    Some("MATERIALIZED")
10920                } else {
10921                    table_modifier.as_deref()
10922                };
10923                self.parse_create_table(or_replace, temporary, leading_comments, modifier)
10924            }
10925            TokenType::Dictionary => {
10926                self.parse_create_table(or_replace, temporary, leading_comments, Some("DICTIONARY"))
10927            }
10928            TokenType::View => self.parse_create_view(
10929                or_replace,
10930                or_alter,
10931                materialized,
10932                temporary,
10933                algorithm,
10934                definer,
10935                security,
10936                secure,
10937            ),
10938            TokenType::Unique => {
10939                self.skip(); // consume UNIQUE
10940                             // Check for CLUSTERED/NONCLUSTERED after UNIQUE (TSQL)
10941                let clustered = if self.check_identifier("CLUSTERED") {
10942                    self.skip();
10943                    Some("CLUSTERED".to_string())
10944                } else if self.check_identifier("NONCLUSTERED") {
10945                    self.skip();
10946                    Some("NONCLUSTERED".to_string())
10947                } else {
10948                    None
10949                };
10950                // Check for COLUMNSTORE (TSQL: CREATE UNIQUE NONCLUSTERED COLUMNSTORE INDEX)
10951                if self.check_identifier("COLUMNSTORE") {
10952                    self.skip();
10953                    // Prepend COLUMNSTORE to clustered
10954                    let clustered = clustered
10955                        .map(|c| format!("{} COLUMNSTORE", c))
10956                        .or_else(|| Some("COLUMNSTORE".to_string()));
10957                    self.parse_create_index_with_clustered(true, clustered)
10958                } else {
10959                    self.parse_create_index_with_clustered(true, clustered)
10960                }
10961            }
10962            TokenType::Index => self.parse_create_index_with_clustered(false, None),
10963            TokenType::Schema => self.parse_create_schema(leading_comments),
10964            TokenType::Database => self.parse_create_database(),
10965            TokenType::Function => {
10966                self.parse_create_function(or_replace, or_alter, temporary, false)
10967            }
10968            TokenType::Procedure => self.parse_create_procedure(or_replace, or_alter),
10969            TokenType::Sequence => self.parse_create_sequence(temporary, or_replace),
10970            TokenType::Trigger => {
10971                self.parse_create_trigger(or_replace, or_alter, false, create_pos)
10972            }
10973            TokenType::Constraint => {
10974                self.skip(); // consume CONSTRAINT
10975                self.parse_create_trigger(or_replace, or_alter, true, create_pos)
10976            }
10977            TokenType::Type => self.parse_create_type(),
10978            TokenType::Domain => self.parse_create_domain(),
10979            _ => {
10980                // Handle TSQL CLUSTERED/NONCLUSTERED [COLUMNSTORE] INDEX
10981                if self.check_identifier("CLUSTERED") || self.check_identifier("NONCLUSTERED") {
10982                    let clustered_text = self.advance().text.to_ascii_uppercase();
10983                    // Check for COLUMNSTORE after CLUSTERED/NONCLUSTERED
10984                    let clustered = if self.check_identifier("COLUMNSTORE") {
10985                        self.skip();
10986                        Some(format!("{} COLUMNSTORE", clustered_text))
10987                    } else {
10988                        Some(clustered_text)
10989                    };
10990                    return self.parse_create_index_with_clustered(false, clustered);
10991                }
10992                // Handle TSQL COLUMNSTORE INDEX (without CLUSTERED/NONCLUSTERED prefix)
10993                if self.check_identifier("COLUMNSTORE") && {
10994                    let pos = self.current;
10995                    let result = pos + 1 < self.tokens.len()
10996                        && self.tokens[pos + 1].token_type == TokenType::Index;
10997                    result
10998                } {
10999                    self.skip(); // consume COLUMNSTORE
11000                                 // COLUMNSTORE without prefix implies NONCLUSTERED
11001                    return self.parse_create_index_with_clustered(
11002                        false,
11003                        Some("NONCLUSTERED COLUMNSTORE".to_string()),
11004                    );
11005                }
11006                // Handle identifiers that aren't keywords: TAG, STAGE, STREAM, etc.
11007                if self.check_identifier("TAG") {
11008                    return self.parse_create_tag(or_replace);
11009                }
11010                if self.check_identifier("STAGE") {
11011                    return self.parse_create_stage(or_replace, temporary);
11012                }
11013                if self.check_identifier("STREAM") {
11014                    return self.parse_create_stream(or_replace);
11015                }
11016                if self.check_identifier("TASK") {
11017                    return self.parse_create_task(or_replace);
11018                }
11019                if (self.check_identifier("FILE") || self.check(TokenType::File)) && {
11020                    let next = self.current + 1;
11021                    next < self.tokens.len()
11022                        && (self.tokens[next].text.eq_ignore_ascii_case("FORMAT"))
11023                } {
11024                    return self.parse_create_file_format(or_replace, temporary);
11025                }
11026                // TSQL: CREATE SYNONYM name FOR target
11027                if self.check_identifier("SYNONYM") {
11028                    self.skip(); // consume SYNONYM
11029                    let name = self.parse_table_ref()?;
11030                    self.expect(TokenType::For)?;
11031                    let target = self.parse_table_ref()?;
11032                    return Ok(Expression::CreateSynonym(Box::new(
11033                        crate::expressions::CreateSynonym { name, target },
11034                    )));
11035                }
11036                // Fall back to Raw for unrecognized CREATE targets
11037                // (e.g., CREATE WAREHOUSE, CREATE STREAMLIT, CREATE STORAGE INTEGRATION, etc.)
11038                {
11039                    let start = self.current;
11040                    while !self.is_at_end() && !self.check(TokenType::Semicolon) {
11041                        self.skip();
11042                    }
11043                    let sql = self.tokens_to_sql(start, self.current);
11044                    let mut prefix = String::from("CREATE");
11045                    if or_replace {
11046                        prefix.push_str(" OR REPLACE");
11047                    }
11048                    if temporary {
11049                        prefix.push_str(" TEMPORARY");
11050                    }
11051                    if materialized {
11052                        prefix.push_str(" MATERIALIZED");
11053                    }
11054                    prefix.push(' ');
11055                    prefix.push_str(&sql);
11056                    Ok(Expression::Raw(Raw { sql: prefix }))
11057                }
11058            }
11059        }
11060    }
11061
11062    /// Parse CREATE TABLE
11063    fn parse_create_table(
11064        &mut self,
11065        or_replace: bool,
11066        temporary: bool,
11067        leading_comments: Vec<String>,
11068        table_modifier: Option<&str>,
11069    ) -> Result<Expression> {
11070        if table_modifier == Some("DICTIONARY") {
11071            let _ = self.match_token(TokenType::Dictionary);
11072        } else {
11073            self.expect(TokenType::Table)?;
11074        }
11075
11076        // Handle IF NOT EXISTS
11077        let if_not_exists =
11078            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
11079
11080        let is_special_modifier = matches!(
11081            table_modifier,
11082            Some(
11083                "DYNAMIC"
11084                    | "ICEBERG"
11085                    | "EXTERNAL"
11086                    | "HYBRID"
11087                    | "UNLOGGED"
11088                    | "DICTIONARY"
11089                    | "MATERIALIZED"
11090            )
11091        ) || (table_modifier.is_some()
11092            && matches!(
11093                self.config.dialect,
11094                Some(crate::dialects::DialectType::Teradata)
11095            ));
11096        let is_clickhouse = matches!(
11097            self.config.dialect,
11098            Some(crate::dialects::DialectType::ClickHouse)
11099        );
11100
11101        // Parse table name
11102        let name = self.parse_table_ref()?;
11103
11104        // ClickHouse: UUID 'xxx' clause after table name
11105        let uuid = if matches!(
11106            self.config.dialect,
11107            Some(crate::dialects::DialectType::ClickHouse)
11108        ) && self.check_identifier("UUID")
11109        {
11110            self.skip(); // consume UUID
11111            let uuid_token = self.advance().clone();
11112            // Strip surrounding quotes from the UUID string
11113            let uuid_text = uuid_token.text.trim_matches('\'').to_string();
11114            Some(uuid_text)
11115        } else {
11116            None
11117        };
11118
11119        // ClickHouse: ON CLUSTER clause
11120        let on_cluster = self.parse_on_cluster_clause()?;
11121
11122        // Teradata: options after name before column list
11123        let teradata_post_name_options = if matches!(
11124            self.config.dialect,
11125            Some(crate::dialects::DialectType::Teradata)
11126        ) {
11127            self.parse_teradata_post_name_options()
11128        } else {
11129            Vec::new()
11130        };
11131
11132        // Handle PARTITION OF parent_table [(column_defs)] [FOR VALUES spec | DEFAULT] [PARTITION BY ...]
11133        if self.match_keywords(&[TokenType::Partition, TokenType::Of]) {
11134            return self.parse_create_table_partition_of(
11135                name,
11136                if_not_exists,
11137                temporary,
11138                or_replace,
11139                table_modifier,
11140                leading_comments,
11141            );
11142        }
11143
11144        // ClickHouse: EMPTY AS source_table — create empty table from source
11145        if matches!(
11146            self.config.dialect,
11147            Some(crate::dialects::DialectType::ClickHouse)
11148        ) && self.check_identifier("EMPTY")
11149        {
11150            if self.check_next(TokenType::As) {
11151                self.skip(); // consume EMPTY
11152                self.skip(); // consume AS
11153                             // Consume rest as Command
11154                let start = self.current;
11155                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
11156                    self.skip();
11157                }
11158                let rest_sql = self.tokens_to_sql(start, self.current);
11159                let mut prefix = String::from("CREATE TABLE");
11160                if if_not_exists {
11161                    prefix.push_str(" IF NOT EXISTS");
11162                }
11163                prefix.push(' ');
11164                prefix.push_str(&name.name.name);
11165                prefix.push_str(" EMPTY AS ");
11166                prefix.push_str(&rest_sql);
11167                return Ok(Expression::Raw(Raw { sql: prefix }));
11168            }
11169        }
11170
11171        // Handle [SHALLOW | DEEP] CLONE source_table [AT(...) | BEFORE(...)]
11172        // Databricks/Delta Lake uses SHALLOW CLONE / DEEP CLONE
11173        // Snowflake uses just CLONE (which is equivalent to DEEP CLONE)
11174        let shallow_clone = self.check_identifier("SHALLOW");
11175        let deep_clone = self.check_identifier("DEEP");
11176        if shallow_clone || deep_clone {
11177            self.skip(); // consume SHALLOW or DEEP
11178        }
11179        // Also handle COPY (BigQuery: CREATE TABLE ... COPY source_table)
11180        // But NOT "COPY GRANTS" which is a Snowflake property
11181        let is_copy = self.check(TokenType::Copy) && !self.check_next_identifier("GRANTS");
11182        if self.check_identifier("CLONE") || is_copy {
11183            self.skip(); // consume CLONE or COPY
11184                         // ClickHouse: CLONE AS source_table (AS is part of the syntax, not an alias)
11185            if matches!(
11186                self.config.dialect,
11187                Some(crate::dialects::DialectType::ClickHouse)
11188            ) {
11189                let _ = self.match_token(TokenType::As);
11190            }
11191            let source = self.parse_table_ref()?;
11192            // Parse optional AT or BEFORE time travel clause
11193            // Note: BEFORE is a keyword token, AT is an identifier
11194            let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
11195                let keyword = self.previous().text.to_ascii_uppercase();
11196                self.expect(TokenType::LParen)?;
11197                // Parse the content: OFFSET => value or TIMESTAMP => value
11198                let mut result = format!("{} (", keyword);
11199                let mut prev_token_type: Option<TokenType> = None;
11200                let mut paren_depth = 1;
11201                while !self.is_at_end() && paren_depth > 0 {
11202                    let token = self.advance();
11203                    if token.token_type == TokenType::LParen {
11204                        paren_depth += 1;
11205                    } else if token.token_type == TokenType::RParen {
11206                        paren_depth -= 1;
11207                        if paren_depth == 0 {
11208                            break;
11209                        }
11210                    }
11211                    let needs_space = !result.ends_with('(')
11212                        && prev_token_type != Some(TokenType::Arrow)
11213                        && prev_token_type != Some(TokenType::Dash)
11214                        && prev_token_type != Some(TokenType::LParen)
11215                        && prev_token_type != Some(TokenType::Comma) // comma already adds trailing space
11216                        && token.token_type != TokenType::LParen; // no space before (
11217                    if needs_space
11218                        && token.token_type != TokenType::RParen
11219                        && token.token_type != TokenType::Comma
11220                    {
11221                        result.push(' ');
11222                    }
11223                    // Properly quote string literals
11224                    if token.token_type == TokenType::String {
11225                        result.push('\'');
11226                        result.push_str(&token.text.replace('\'', "''"));
11227                        result.push('\'');
11228                    } else {
11229                        result.push_str(&token.text);
11230                    }
11231                    if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma
11232                    {
11233                        result.push(' ');
11234                    }
11235                    prev_token_type = Some(token.token_type);
11236                }
11237                result.push(')');
11238                Some(Expression::Raw(Raw { sql: result }))
11239            } else {
11240                None
11241            };
11242            // Return the CLONE table immediately
11243            return Ok(Expression::CreateTable(Box::new(CreateTable {
11244                name,
11245                on_cluster: on_cluster.clone(),
11246                columns: Vec::new(),
11247                constraints: Vec::new(),
11248                if_not_exists,
11249                temporary,
11250                or_replace,
11251                table_modifier: table_modifier.map(|s| s.to_string()),
11252                as_select: None,
11253                as_select_parenthesized: false,
11254                on_commit: None,
11255                clone_source: Some(source),
11256                clone_at_clause: at_clause,
11257                shallow_clone,
11258                is_copy,
11259                leading_comments,
11260                with_properties: Vec::new(),
11261                teradata_post_name_options: teradata_post_name_options.clone(),
11262                with_data: None,
11263                with_statistics: None,
11264                teradata_indexes: Vec::new(),
11265                with_cte: None,
11266                properties: Vec::new(),
11267                partition_of: None,
11268                post_table_properties: Vec::new(),
11269                mysql_table_options: Vec::new(),
11270                inherits: Vec::new(),
11271                on_property: None,
11272                copy_grants: false,
11273                using_template: None,
11274                rollup: None,
11275                uuid: uuid.clone(),
11276            })));
11277        }
11278
11279        // Handle WITH properties before columns/AS (e.g., CREATE TABLE z WITH (FORMAT='parquet') AS SELECT 1)
11280        let with_properties = if self.match_token(TokenType::With) {
11281            self.parse_with_properties()?
11282        } else {
11283            Vec::new()
11284        };
11285
11286        // Snowflake: COPY GRANTS clause (before column list or AS)
11287        let copy_grants = self.match_text_seq(&["COPY", "GRANTS"]);
11288
11289        // Snowflake: USING TEMPLATE (expr) - allows schema inference from a query
11290        let using_template = if self.match_text_seq(&["USING", "TEMPLATE"]) {
11291            Some(Box::new(self.parse_primary()?))
11292        } else {
11293            None
11294        };
11295
11296        // If we have USING TEMPLATE, return early since it replaces AS SELECT
11297        if using_template.is_some() {
11298            return Ok(Expression::CreateTable(Box::new(CreateTable {
11299                name,
11300                on_cluster: on_cluster.clone(),
11301                columns: Vec::new(),
11302                constraints: Vec::new(),
11303                if_not_exists,
11304                temporary,
11305                or_replace,
11306                table_modifier: table_modifier.map(|s| s.to_string()),
11307                as_select: None,
11308                as_select_parenthesized: false,
11309                on_commit: None,
11310                clone_source: None,
11311                clone_at_clause: None,
11312                shallow_clone: false,
11313                is_copy: false,
11314                leading_comments,
11315                with_properties,
11316                teradata_post_name_options: teradata_post_name_options.clone(),
11317                with_data: None,
11318                with_statistics: None,
11319                teradata_indexes: Vec::new(),
11320                with_cte: None,
11321                properties: Vec::new(),
11322                partition_of: None,
11323                post_table_properties: Vec::new(),
11324                mysql_table_options: Vec::new(),
11325                inherits: Vec::new(),
11326                on_property: None,
11327                copy_grants,
11328                using_template,
11329                rollup: None,
11330                uuid: uuid.clone(),
11331            })));
11332        }
11333
11334        // Redshift: Parse DISTKEY, SORTKEY, DISTSTYLE, BACKUP before AS SELECT (CTAS without columns)
11335        // This handles: CREATE TABLE t BACKUP YES|NO AS SELECT ...
11336        let mut redshift_ctas_properties: Vec<Expression> = Vec::new();
11337        loop {
11338            if self.match_identifier("DISTKEY") {
11339                // DISTKEY(column)
11340                if self.match_token(TokenType::LParen) {
11341                    let col = self.expect_identifier()?;
11342                    self.expect(TokenType::RParen)?;
11343                    redshift_ctas_properties.push(Expression::DistKeyProperty(Box::new(
11344                        DistKeyProperty {
11345                            this: Box::new(Expression::boxed_column(Column {
11346                                name: Identifier::new(col),
11347                                table: None,
11348                                join_mark: false,
11349                                trailing_comments: Vec::new(),
11350                                span: None,
11351                                inferred_type: None,
11352                            })),
11353                        },
11354                    )));
11355                }
11356            } else if self.check_identifier("COMPOUND") || self.check_identifier("INTERLEAVED") {
11357                // COMPOUND SORTKEY(col, ...) or INTERLEAVED SORTKEY(col, ...)
11358                let modifier = self.advance().text.to_ascii_uppercase();
11359                if self.match_identifier("SORTKEY") && self.match_token(TokenType::LParen) {
11360                    let mut cols = Vec::new();
11361                    loop {
11362                        let col = self.expect_identifier()?;
11363                        cols.push(Expression::boxed_column(Column {
11364                            name: Identifier::new(col),
11365                            table: None,
11366                            join_mark: false,
11367                            trailing_comments: Vec::new(),
11368                            span: None,
11369                            inferred_type: None,
11370                        }));
11371                        if !self.match_token(TokenType::Comma) {
11372                            break;
11373                        }
11374                    }
11375                    self.expect(TokenType::RParen)?;
11376                    let compound_value = if modifier == "COMPOUND" {
11377                        Some(Box::new(Expression::Boolean(BooleanLiteral {
11378                            value: true,
11379                        })))
11380                    } else {
11381                        None
11382                    };
11383                    redshift_ctas_properties.push(Expression::SortKeyProperty(Box::new(
11384                        SortKeyProperty {
11385                            this: Box::new(Expression::Tuple(Box::new(Tuple {
11386                                expressions: cols,
11387                            }))),
11388                            compound: compound_value,
11389                        },
11390                    )));
11391                }
11392            } else if self.match_identifier("SORTKEY") {
11393                // SORTKEY(column, ...)
11394                if self.match_token(TokenType::LParen) {
11395                    let mut cols = Vec::new();
11396                    loop {
11397                        let col = self.expect_identifier()?;
11398                        cols.push(Expression::boxed_column(Column {
11399                            name: Identifier::new(col),
11400                            table: None,
11401                            join_mark: false,
11402                            trailing_comments: Vec::new(),
11403                            span: None,
11404                            inferred_type: None,
11405                        }));
11406                        if !self.match_token(TokenType::Comma) {
11407                            break;
11408                        }
11409                    }
11410                    self.expect(TokenType::RParen)?;
11411                    redshift_ctas_properties.push(Expression::SortKeyProperty(Box::new(
11412                        SortKeyProperty {
11413                            this: Box::new(Expression::Tuple(Box::new(Tuple {
11414                                expressions: cols,
11415                            }))),
11416                            compound: None,
11417                        },
11418                    )));
11419                }
11420            } else if self.match_identifier("DISTSTYLE") {
11421                // DISTSTYLE ALL|EVEN|AUTO|KEY
11422                if self.match_texts(&["ALL", "EVEN", "AUTO", "KEY"]) {
11423                    let style = self.previous().text.to_ascii_uppercase();
11424                    redshift_ctas_properties.push(Expression::DistStyleProperty(Box::new(
11425                        DistStyleProperty {
11426                            this: Box::new(Expression::Var(Box::new(Var { this: style }))),
11427                        },
11428                    )));
11429                }
11430            } else if self.match_identifier("BACKUP") {
11431                // BACKUP YES|NO
11432                if self.match_texts(&["YES", "NO"]) {
11433                    let value = self.previous().text.to_ascii_uppercase();
11434                    redshift_ctas_properties.push(Expression::BackupProperty(Box::new(
11435                        BackupProperty {
11436                            this: Box::new(Expression::Var(Box::new(Var { this: value }))),
11437                        },
11438                    )));
11439                }
11440            } else {
11441                break;
11442            }
11443        }
11444
11445        // Check for AS SELECT (CTAS)
11446        if self.match_token(TokenType::As) {
11447            // ClickHouse: CREATE TABLE t AS other_table [ENGINE = ...] — copy structure from another table
11448            // Also: CREATE TABLE t AS func_name(args...) — table from function (e.g., remote, merge)
11449            // Detect when AS is followed by an identifier (not SELECT/WITH/LParen)
11450            if is_clickhouse
11451                && !self.check(TokenType::Select)
11452                && !self.check(TokenType::With)
11453                && !self.check(TokenType::LParen)
11454                && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
11455            {
11456                // Check if this is AS func_name(...) — table function
11457                let is_table_func = self.current + 1 < self.tokens.len()
11458                    && self.tokens[self.current + 1].token_type == TokenType::LParen;
11459                let source = if is_table_func {
11460                    // Parse as expression to consume function call with arguments
11461                    self.parse_primary()?;
11462                    let mut table_properties: Vec<Expression> = Vec::new();
11463                    self.parse_clickhouse_table_properties(&mut table_properties)?;
11464                    return Ok(Expression::CreateTable(Box::new(CreateTable {
11465                        name,
11466                        on_cluster: on_cluster.clone(),
11467                        columns: Vec::new(),
11468                        constraints: Vec::new(),
11469                        if_not_exists,
11470                        temporary,
11471                        or_replace,
11472                        table_modifier: table_modifier.map(|s| s.to_string()),
11473                        as_select: None,
11474                        as_select_parenthesized: false,
11475                        on_commit: None,
11476                        clone_source: None,
11477                        clone_at_clause: None,
11478                        shallow_clone: false,
11479                        is_copy: false,
11480                        leading_comments,
11481                        with_properties,
11482                        teradata_post_name_options: teradata_post_name_options.clone(),
11483                        with_data: None,
11484                        with_statistics: None,
11485                        teradata_indexes: Vec::new(),
11486                        with_cte: None,
11487                        properties: table_properties,
11488                        partition_of: None,
11489                        post_table_properties: redshift_ctas_properties,
11490                        mysql_table_options: Vec::new(),
11491                        inherits: Vec::new(),
11492                        on_property: None,
11493                        copy_grants,
11494                        using_template: None,
11495                        rollup: None,
11496                        uuid: uuid.clone(),
11497                    })));
11498                } else {
11499                    self.parse_table_ref()?
11500                };
11501                // Parse ClickHouse table properties after the source table
11502                let mut table_properties: Vec<Expression> = Vec::new();
11503                self.parse_clickhouse_table_properties(&mut table_properties)?;
11504                return Ok(Expression::CreateTable(Box::new(CreateTable {
11505                    name,
11506                    on_cluster: on_cluster.clone(),
11507                    columns: Vec::new(),
11508                    constraints: Vec::new(),
11509                    if_not_exists,
11510                    temporary,
11511                    or_replace,
11512                    table_modifier: table_modifier.map(|s| s.to_string()),
11513                    as_select: None,
11514                    as_select_parenthesized: false,
11515                    on_commit: None,
11516                    clone_source: Some(source),
11517                    clone_at_clause: None,
11518                    shallow_clone: false,
11519                    is_copy: false,
11520                    leading_comments,
11521                    with_properties,
11522                    teradata_post_name_options: teradata_post_name_options.clone(),
11523                    with_data: None,
11524                    with_statistics: None,
11525                    teradata_indexes: Vec::new(),
11526                    with_cte: None,
11527                    properties: table_properties,
11528                    partition_of: None,
11529                    post_table_properties: redshift_ctas_properties,
11530                    mysql_table_options: Vec::new(),
11531                    inherits: Vec::new(),
11532                    on_property: None,
11533                    copy_grants,
11534                    using_template: None,
11535                    rollup: None,
11536                    uuid: uuid.clone(),
11537                })));
11538            }
11539
11540            // The query can be:
11541            // - SELECT ... (simple case)
11542            // - (SELECT 1) UNION ALL (SELECT 2) (set operations)
11543            // - (WITH cte AS (SELECT 1) SELECT * FROM cte) (CTE in parens)
11544            let mut as_select_parenthesized = self.check(TokenType::LParen);
11545            let query = if as_select_parenthesized {
11546                // Parenthesized query - parse as expression which handles subqueries
11547                // Note: parse_primary will consume set operations like UNION internally
11548                let subquery = self.parse_primary()?;
11549                // If parse_primary returned a set operation, the outer parens weren't wrapping
11550                // the entire expression - they were part of the operands
11551                if matches!(
11552                    &subquery,
11553                    Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
11554                ) {
11555                    as_select_parenthesized = false;
11556                    subquery
11557                } else {
11558                    // Just a parenthesized query without set ops
11559                    // Keep the Subquery wrapper if it has limit/offset/order_by
11560                    if let Expression::Subquery(ref sq) = subquery {
11561                        if sq.limit.is_some() || sq.offset.is_some() || sq.order_by.is_some() {
11562                            // Keep the Subquery to preserve the modifiers
11563                            subquery
11564                        } else {
11565                            // Extract the inner query
11566                            if let Expression::Subquery(sq) = subquery {
11567                                sq.this
11568                            } else {
11569                                subquery
11570                            }
11571                        }
11572                    } else if let Expression::Paren(p) = subquery {
11573                        p.this
11574                    } else {
11575                        subquery
11576                    }
11577                }
11578            } else if self.check(TokenType::With) {
11579                // Handle WITH ... SELECT ...
11580                self.parse_statement()?
11581            } else {
11582                self.parse_select()?
11583            };
11584
11585            // Parse any trailing Teradata options like "WITH DATA", "NO PRIMARY INDEX", etc.
11586            let (with_data, with_statistics, teradata_indexes) =
11587                self.parse_teradata_table_options();
11588            let on_commit = if matches!(
11589                self.config.dialect,
11590                Some(crate::dialects::DialectType::Teradata)
11591            ) && self.check(TokenType::On)
11592                && self.check_next(TokenType::Commit)
11593            {
11594                self.skip(); // ON
11595                self.skip(); // COMMIT
11596                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
11597                    Some(OnCommit::PreserveRows)
11598                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
11599                    Some(OnCommit::DeleteRows)
11600                } else {
11601                    return Err(
11602                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
11603                    );
11604                }
11605            } else {
11606                None
11607            };
11608
11609            return Ok(Expression::CreateTable(Box::new(CreateTable {
11610                name,
11611                on_cluster: on_cluster.clone(),
11612                columns: Vec::new(),
11613                constraints: Vec::new(),
11614                if_not_exists,
11615                temporary,
11616                or_replace,
11617                table_modifier: table_modifier.map(|s| s.to_string()),
11618                as_select: Some(query),
11619                as_select_parenthesized,
11620                on_commit,
11621                clone_source: None,
11622                clone_at_clause: None,
11623                shallow_clone: false,
11624                is_copy: false,
11625                leading_comments,
11626                with_properties,
11627                teradata_post_name_options: teradata_post_name_options.clone(),
11628                with_data,
11629                with_statistics,
11630                teradata_indexes,
11631                with_cte: None,
11632                properties: Vec::new(),
11633                partition_of: None,
11634                post_table_properties: redshift_ctas_properties,
11635                mysql_table_options: Vec::new(),
11636                inherits: Vec::new(),
11637                on_property: None,
11638                copy_grants,
11639                using_template: None,
11640                rollup: None,
11641                uuid: uuid.clone(),
11642            })));
11643        }
11644
11645        // ClickHouse: allow table properties/AS SELECT without a column list
11646        if is_clickhouse && !self.check(TokenType::LParen) {
11647            let starts_props = self.check_identifier("ENGINE")
11648                || self.check(TokenType::Order)
11649                || self.check(TokenType::Sample)
11650                || self.check(TokenType::Settings)
11651                || self.check(TokenType::Comment)
11652                || self.check(TokenType::As);
11653
11654            if starts_props {
11655                let mut table_properties: Vec<Expression> = Vec::new();
11656                self.parse_clickhouse_table_properties(&mut table_properties)?;
11657
11658                let as_select = if self.match_token(TokenType::As) {
11659                    Some(self.parse_statement()?)
11660                } else {
11661                    None
11662                };
11663                let as_select_parenthesized = as_select.is_some();
11664
11665                if as_select.is_some() {
11666                    self.parse_clickhouse_table_properties(&mut table_properties)?;
11667                }
11668
11669                return Ok(Expression::CreateTable(Box::new(CreateTable {
11670                    name,
11671                    on_cluster: on_cluster.clone(),
11672                    columns: Vec::new(),
11673                    constraints: Vec::new(),
11674                    if_not_exists,
11675                    temporary,
11676                    or_replace,
11677                    table_modifier: table_modifier.map(|s| s.to_string()),
11678                    as_select,
11679                    as_select_parenthesized,
11680                    on_commit: None,
11681                    clone_source: None,
11682                    clone_at_clause: None,
11683                    shallow_clone: false,
11684                    is_copy: false,
11685                    leading_comments,
11686                    with_properties,
11687                    teradata_post_name_options: teradata_post_name_options.clone(),
11688                    with_data: None,
11689                    with_statistics: None,
11690                    teradata_indexes: Vec::new(),
11691                    with_cte: None,
11692                    properties: table_properties,
11693                    partition_of: None,
11694                    post_table_properties: Vec::new(),
11695                    mysql_table_options: Vec::new(),
11696                    inherits: Vec::new(),
11697                    on_property: None,
11698                    copy_grants,
11699                    using_template: None,
11700                    rollup: None,
11701                    uuid: uuid.clone(),
11702                })));
11703            }
11704        }
11705
11706        // For DYNAMIC/ICEBERG/EXTERNAL tables, columns might be optional (use AS SELECT or other syntax)
11707        // Check if we have a left paren for columns or if we're going straight to options
11708        if !self.check(TokenType::LParen) && is_special_modifier {
11709            // No columns - parse options and AS SELECT
11710            let mut extra_options = Vec::new();
11711            // Parse key=value options until AS or end
11712            // Note: WAREHOUSE is a keyword token type, so check for it explicitly
11713            while !self.is_at_end()
11714                && !self.check(TokenType::As)
11715                && !self.check(TokenType::Semicolon)
11716            {
11717                if self.is_identifier_token()
11718                    || self.is_safe_keyword_as_identifier()
11719                    || self.check(TokenType::Warehouse)
11720                {
11721                    let key = self.advance().text;
11722                    if self.match_token(TokenType::Eq) {
11723                        // Capture value
11724                        let value = if self.check(TokenType::String) {
11725                            let v = format!("'{}'", self.peek().text);
11726                            self.skip();
11727                            v
11728                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
11729                        {
11730                            self.advance().text
11731                        } else {
11732                            break;
11733                        };
11734                        extra_options.push((key, value));
11735                    } else {
11736                        // Just a keyword without value (like WAREHOUSE mywh)
11737                        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
11738                            let value = self.advance().text;
11739                            extra_options.push((key, value));
11740                        }
11741                    }
11742                } else {
11743                    break;
11744                }
11745            }
11746            // Check for AS SELECT
11747            let as_select = if self.match_token(TokenType::As) {
11748                Some(self.parse_statement()?)
11749            } else {
11750                None
11751            };
11752            return Ok(Expression::CreateTable(Box::new(CreateTable {
11753                name,
11754                on_cluster: on_cluster.clone(),
11755                columns: Vec::new(),
11756                constraints: Vec::new(),
11757                if_not_exists,
11758                temporary,
11759                or_replace,
11760                table_modifier: table_modifier.map(|s| s.to_string()),
11761                as_select,
11762                as_select_parenthesized: false,
11763                on_commit: None,
11764                clone_source: None,
11765                clone_at_clause: None,
11766                shallow_clone: false,
11767                is_copy: false,
11768                leading_comments,
11769                with_properties: extra_options,
11770                teradata_post_name_options: teradata_post_name_options.clone(),
11771                with_data: None,
11772                with_statistics: None,
11773                teradata_indexes: Vec::new(),
11774                with_cte: None,
11775                properties: Vec::new(),
11776                partition_of: None,
11777                post_table_properties: Vec::new(),
11778                mysql_table_options: Vec::new(),
11779                inherits: Vec::new(),
11780                on_property: None,
11781                copy_grants,
11782                using_template: None,
11783                rollup: None,
11784                uuid: uuid.clone(),
11785            })));
11786        }
11787
11788        // MySQL: CREATE TABLE A LIKE B (without parentheses)
11789        if self.check(TokenType::Like) {
11790            self.skip(); // consume LIKE
11791            let source_ref = self.parse_table_ref()?;
11792            return Ok(Expression::CreateTable(Box::new(CreateTable {
11793                name,
11794                on_cluster: on_cluster.clone(),
11795                columns: Vec::new(),
11796                constraints: vec![TableConstraint::Like {
11797                    source: source_ref,
11798                    options: Vec::new(),
11799                }],
11800                if_not_exists,
11801                temporary,
11802                or_replace,
11803                table_modifier: table_modifier.map(|s| s.to_string()),
11804                as_select: None,
11805                as_select_parenthesized: false,
11806                on_commit: None,
11807                clone_source: None,
11808                clone_at_clause: None,
11809                shallow_clone: false,
11810                is_copy: false,
11811                leading_comments,
11812                with_properties,
11813                teradata_post_name_options: teradata_post_name_options.clone(),
11814                with_data: None,
11815                with_statistics: None,
11816                teradata_indexes: Vec::new(),
11817                with_cte: None,
11818                properties: Vec::new(),
11819                partition_of: None,
11820                post_table_properties: Vec::new(),
11821                mysql_table_options: Vec::new(),
11822                inherits: Vec::new(),
11823                on_property: None,
11824                copy_grants,
11825                using_template: None,
11826                rollup: None,
11827                uuid: uuid.clone(),
11828            })));
11829        }
11830
11831        // Snowflake: CREATE TABLE a TAG (key='value', ...) without column definitions
11832        if self.match_keyword("TAG")
11833            || (self.match_token(TokenType::With) && self.match_keyword("TAG"))
11834        {
11835            let tags = self.parse_tags()?;
11836            return Ok(Expression::CreateTable(Box::new(CreateTable {
11837                name,
11838                on_cluster: on_cluster.clone(),
11839                columns: Vec::new(),
11840                constraints: vec![TableConstraint::Tags(tags)],
11841                if_not_exists,
11842                temporary,
11843                or_replace,
11844                table_modifier: table_modifier.map(|s| s.to_string()),
11845                as_select: None,
11846                as_select_parenthesized: false,
11847                on_commit: None,
11848                clone_source: None,
11849                clone_at_clause: None,
11850                shallow_clone: false,
11851                is_copy: false,
11852                leading_comments,
11853                with_properties,
11854                teradata_post_name_options: teradata_post_name_options.clone(),
11855                with_data: None,
11856                with_statistics: None,
11857                teradata_indexes: Vec::new(),
11858                with_cte: None,
11859                properties: Vec::new(),
11860                partition_of: None,
11861                post_table_properties: Vec::new(),
11862                mysql_table_options: Vec::new(),
11863                inherits: Vec::new(),
11864                on_property: None,
11865                copy_grants,
11866                using_template: None,
11867                rollup: None,
11868                uuid: uuid.clone(),
11869            })));
11870        }
11871
11872        // Hive/Spark/Databricks: CREATE TABLE t TBLPROPERTIES (...) without column definitions
11873        // Check for Hive-style table properties before expecting column definitions
11874        if self.check_identifier("TBLPROPERTIES")
11875            || self.check_identifier("LOCATION")
11876            || self.check_identifier("STORED")
11877            || self.check(TokenType::Row)
11878            || self.check(TokenType::Using)
11879            || self.check_identifier("CLUSTERED")
11880            || self.check_identifier("PARTITIONED")
11881            || self.check_identifier("COMMENT")
11882        {
11883            // Parse Hive table properties without column definitions
11884            let hive_properties = self.parse_hive_table_properties()?;
11885
11886            // Check for AS SELECT (CTAS) after properties
11887            let as_select = if self.match_token(TokenType::As) {
11888                Some(self.parse_statement()?)
11889            } else {
11890                None
11891            };
11892
11893            return Ok(Expression::CreateTable(Box::new(CreateTable {
11894                name,
11895                on_cluster: on_cluster.clone(),
11896                columns: Vec::new(),
11897                constraints: Vec::new(),
11898                if_not_exists,
11899                temporary,
11900                or_replace,
11901                table_modifier: table_modifier.map(|s| s.to_string()),
11902                as_select,
11903                as_select_parenthesized: false,
11904                on_commit: None,
11905                clone_source: None,
11906                clone_at_clause: None,
11907                shallow_clone: false,
11908                is_copy: false,
11909                leading_comments,
11910                with_properties,
11911                teradata_post_name_options: teradata_post_name_options.clone(),
11912                with_data: None,
11913                with_statistics: None,
11914                teradata_indexes: Vec::new(),
11915                with_cte: None,
11916                properties: hive_properties,
11917                partition_of: None,
11918                post_table_properties: Vec::new(),
11919                mysql_table_options: Vec::new(),
11920                inherits: Vec::new(),
11921                on_property: None,
11922                copy_grants,
11923                using_template: None,
11924                rollup: None,
11925                uuid: uuid.clone(),
11926            })));
11927        }
11928
11929        // Check if (SELECT ...) or (WITH ...) follows - this is CTAS without explicit AS keyword
11930        if self.check(TokenType::LParen) {
11931            let saved = self.current;
11932            self.skip(); // consume (
11933            let is_ctas = self.check(TokenType::Select) || self.check(TokenType::With);
11934            self.current = saved;
11935            if is_ctas {
11936                // Parse as subquery
11937                let subquery = self.parse_primary()?;
11938                let query = if let Expression::Subquery(sq) = subquery {
11939                    sq.this
11940                } else if let Expression::Paren(p) = subquery {
11941                    p.this
11942                } else {
11943                    subquery
11944                };
11945                return Ok(Expression::CreateTable(Box::new(CreateTable {
11946                    name,
11947                    on_cluster: on_cluster.clone(),
11948                    columns: Vec::new(),
11949                    constraints: Vec::new(),
11950                    if_not_exists,
11951                    temporary,
11952                    or_replace,
11953                    table_modifier: table_modifier.map(|s| s.to_string()),
11954                    as_select: Some(query),
11955                    as_select_parenthesized: true,
11956                    on_commit: None,
11957                    clone_source: None,
11958                    clone_at_clause: None,
11959                    shallow_clone: false,
11960                    is_copy: false,
11961                    leading_comments,
11962                    with_properties,
11963                    teradata_post_name_options: teradata_post_name_options.clone(),
11964                    with_data: None,
11965                    with_statistics: None,
11966                    teradata_indexes: Vec::new(),
11967                    with_cte: None,
11968                    properties: Vec::new(),
11969                    partition_of: None,
11970                    post_table_properties: Vec::new(),
11971                    mysql_table_options: Vec::new(),
11972                    inherits: Vec::new(),
11973                    on_property: None,
11974                    copy_grants,
11975                    using_template: None,
11976                    rollup: None,
11977                    uuid: uuid.clone(),
11978                })));
11979            }
11980        }
11981
11982        // BigQuery (and others): CREATE TABLE t PARTITION BY ... CLUSTER BY ... OPTIONS(...) AS (SELECT ...)
11983        // When there are no column definitions, skip straight to property/AS parsing
11984        let no_column_defs = !self.check(TokenType::LParen)
11985            && (self.check(TokenType::Partition)
11986                || self.check(TokenType::PartitionBy)
11987                || self.check(TokenType::Cluster)
11988                || self.check_identifier("OPTIONS")
11989                || self.check(TokenType::As));
11990
11991        // Parse column definitions
11992        if !no_column_defs {
11993            self.expect(TokenType::LParen)?;
11994        }
11995
11996        // For DYNAMIC TABLE, column list contains only names without types
11997        // e.g., CREATE DYNAMIC TABLE t (col1, col2, col3) TARGET_LAG=... AS SELECT ...
11998        let (columns, constraints) = if no_column_defs {
11999            (Vec::new(), Vec::new())
12000        } else if table_modifier == Some("DYNAMIC") {
12001            // Check if this looks like a simple column name list (just identifiers separated by commas)
12002            // by peeking ahead - if next token after identifier is comma or rparen, it's a name-only list
12003            let saved = self.current;
12004            let is_name_only_list =
12005                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
12006                    self.skip();
12007                    let result = self.check(TokenType::Comma) || self.check(TokenType::RParen);
12008                    self.current = saved;
12009                    result
12010                } else {
12011                    false
12012                };
12013
12014            if is_name_only_list {
12015                // Parse column names without types
12016                let mut cols = Vec::new();
12017                loop {
12018                    let name = self.expect_identifier_or_safe_keyword_with_quoted()?;
12019                    // Create a column def with an empty/placeholder type
12020                    let mut col_def = ColumnDef::new(
12021                        name.name.clone(),
12022                        DataType::Custom {
12023                            name: String::new(),
12024                        },
12025                    );
12026                    col_def.name = name;
12027                    cols.push(col_def);
12028                    if !self.match_token(TokenType::Comma) {
12029                        break;
12030                    }
12031                }
12032                (cols, Vec::new())
12033            } else {
12034                // Regular column definitions with types
12035                self.parse_column_definitions()?
12036            }
12037        } else {
12038            self.parse_column_definitions()?
12039        };
12040
12041        if !no_column_defs {
12042            self.expect(TokenType::RParen)?;
12043        }
12044
12045        // Parse COMMENT before WITH properties (Presto: CREATE TABLE x (...) COMMENT 'text' WITH (...))
12046        let pre_with_comment = if self.check(TokenType::Comment) {
12047            let saved = self.current;
12048            self.skip(); // consume COMMENT
12049            if self.check(TokenType::String) {
12050                let comment_text = self.advance().text.clone();
12051                Some(comment_text)
12052            } else {
12053                self.current = saved;
12054                None
12055            }
12056        } else {
12057            None
12058        };
12059
12060        // Handle WITH properties after columns (e.g., CREATE TABLE z (z INT) WITH (...))
12061        // But skip if this is WITH(SYSTEM_VERSIONING=...) which is handled by parse_post_table_properties
12062        let with_properties_after = if self.check(TokenType::With) {
12063            // Lookahead: check if this is WITH(SYSTEM_VERSIONING=...)
12064            let saved = self.current;
12065            self.skip(); // consume WITH
12066            let is_system_versioning = if self.check(TokenType::LParen) {
12067                let saved2 = self.current;
12068                self.skip(); // consume (
12069                let result = self.check_identifier("SYSTEM_VERSIONING");
12070                self.current = saved2; // retreat to before (
12071                result
12072            } else {
12073                false
12074            };
12075            if is_system_versioning {
12076                // Retreat back before WITH, let parse_post_table_properties handle it
12077                self.current = saved;
12078                Vec::new()
12079            } else {
12080                // Normal WITH properties parsing
12081                self.parse_with_properties()?
12082            }
12083        } else {
12084            Vec::new()
12085        };
12086
12087        // Combine properties from before and after columns
12088        let mut all_with_properties = with_properties;
12089        all_with_properties.extend(with_properties_after);
12090
12091        // For DYNAMIC/ICEBERG/EXTERNAL tables with columns, parse Snowflake-specific options
12092        // like TARGET_LAG, WAREHOUSE, CATALOG, EXTERNAL_VOLUME, LOCATION etc.
12093        if is_special_modifier {
12094            while !self.is_at_end()
12095                && !self.check(TokenType::As)
12096                && !self.check(TokenType::Semicolon)
12097            {
12098                // Check for known Snowflake table options (WAREHOUSE is a keyword, others are identifiers)
12099                // These are Snowflake-style options that use KEY=VALUE or KEY VALUE (without =)
12100                // Hive-style LOCATION/TBLPROPERTIES (without =) should NOT be matched here
12101                let is_snowflake_option = self.check(TokenType::Warehouse)
12102                    || self.check_identifier("TARGET_LAG")
12103                    || self.check_identifier("CATALOG")
12104                    || self.check_identifier("EXTERNAL_VOLUME")
12105                    || self.check_identifier("BASE_LOCATION")
12106                    || self.check_identifier("REFRESH_MODE")
12107                    || self.check_identifier("INITIALIZE")
12108                    || self.check_identifier("DATA_RETENTION_TIME_IN_DAYS")
12109                    || self.check_identifier("LOCATION")
12110                    || self.check_identifier("PARTITION")
12111                    || self.check_identifier("FILE_FORMAT")
12112                    || self.check_identifier("AUTO_REFRESH");
12113                if is_snowflake_option {
12114                    // Save position before consuming key - we might need to retreat for Hive-style syntax
12115                    let saved = self.current;
12116                    let key = self.advance().text;
12117                    if self.match_token(TokenType::Eq) {
12118                        // Capture value - could be string, identifier, stage path @..., keyword, or parenthesized options
12119                        let value = if self.check(TokenType::LParen) {
12120                            // Parenthesized option list like file_format = (type = parquet compression = gzip)
12121                            self.skip(); // consume (
12122                            let mut options = String::from("(");
12123                            let mut depth = 1;
12124                            while !self.is_at_end() && depth > 0 {
12125                                let tok = self.advance();
12126                                if tok.token_type == TokenType::LParen {
12127                                    depth += 1;
12128                                } else if tok.token_type == TokenType::RParen {
12129                                    depth -= 1;
12130                                }
12131                                // Add space before tokens that need it (not after open paren, not before close paren)
12132                                if !options.ends_with('(')
12133                                    && !options.ends_with(' ')
12134                                    && tok.token_type != TokenType::RParen
12135                                {
12136                                    options.push(' ');
12137                                }
12138                                options.push_str(&tok.text);
12139                            }
12140                            options
12141                        } else if self.check(TokenType::String) {
12142                            let v = format!("'{}'", self.peek().text);
12143                            self.skip();
12144                            v
12145                        } else if self.check(TokenType::DAt) {
12146                            // Stage path like @s1/logs/
12147                            self.skip(); // consume @
12148                            let mut path = String::from("@");
12149                            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
12150                                path.push_str(&self.advance().text);
12151                            }
12152                            // Parse path segments, but stop before Snowflake option keywords
12153                            while self.check(TokenType::Slash) {
12154                                // Peek ahead to see if next identifier is a Snowflake option keyword
12155                                if self.current + 1 < self.tokens.len() {
12156                                    let next = &self.tokens[self.current + 1];
12157                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12158                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12159                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12160                                        || next.text.eq_ignore_ascii_case("LOCATION")
12161                                        || next.text.eq_ignore_ascii_case("PARTITION")
12162                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12163                                    {
12164                                        // Consume the trailing slash before the keyword
12165                                        self.skip();
12166                                        path.push('/');
12167                                        break;
12168                                    }
12169                                }
12170                                self.skip();
12171                                path.push('/');
12172                                if self.is_identifier_token()
12173                                    || self.is_safe_keyword_as_identifier()
12174                                {
12175                                    path.push_str(&self.advance().text);
12176                                }
12177                            }
12178                            path
12179                        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
12180                            // Stage path tokenized as Var (e.g., @s2/logs/)
12181                            // When @ is followed by alphanumeric, tokenizer creates a Var token
12182                            let mut path = self.advance().text;
12183                            // Parse path segments, but stop before Snowflake option keywords
12184                            while self.check(TokenType::Slash) {
12185                                // Peek ahead to see if next identifier is a Snowflake option keyword
12186                                if self.current + 1 < self.tokens.len() {
12187                                    let next = &self.tokens[self.current + 1];
12188                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12189                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12190                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12191                                        || next.text.eq_ignore_ascii_case("LOCATION")
12192                                        || next.text.eq_ignore_ascii_case("PARTITION")
12193                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12194                                    {
12195                                        // Consume the trailing slash before the keyword
12196                                        self.skip();
12197                                        path.push('/');
12198                                        break;
12199                                    }
12200                                }
12201                                self.skip();
12202                                path.push('/');
12203                                if self.is_identifier_token()
12204                                    || self.is_safe_keyword_as_identifier()
12205                                {
12206                                    path.push_str(&self.advance().text);
12207                                }
12208                            }
12209                            path
12210                        } else if self.check(TokenType::Warehouse) {
12211                            self.advance().text
12212                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
12213                        {
12214                            self.advance().text
12215                        } else {
12216                            // No valid value after =, retreat and let Hive parsing try
12217                            self.current = saved;
12218                            break;
12219                        };
12220                        all_with_properties.push((key, value));
12221                    } else if self.is_identifier_token()
12222                        || self.is_safe_keyword_as_identifier()
12223                        || self.check(TokenType::Warehouse)
12224                    {
12225                        // WAREHOUSE mywh (without =)
12226                        let value = self.advance().text;
12227                        all_with_properties.push((key, value));
12228                    } else {
12229                        // Not a Snowflake-style option (e.g., Hive LOCATION 'path' without =)
12230                        // Retreat and let Hive parsing try
12231                        self.current = saved;
12232                        break;
12233                    }
12234                } else {
12235                    break;
12236                }
12237            }
12238        }
12239
12240        // Parse MySQL table options: ENGINE=val, AUTO_INCREMENT=val, DEFAULT CHARSET=val, etc.
12241        let mysql_table_options = if is_clickhouse {
12242            Vec::new()
12243        } else {
12244            self.parse_mysql_table_options()
12245        };
12246
12247        // Parse StarRocks ROLLUP property: ROLLUP (r1(col1, col2), r2(col1))
12248        let rollup = if self.match_token(TokenType::Rollup) {
12249            self.expect(TokenType::LParen)?;
12250            let mut indices = Vec::new();
12251            loop {
12252                let name = self.expect_identifier_or_keyword_with_quoted()?;
12253                let cols = if self.match_token(TokenType::LParen) {
12254                    let mut col_list = Vec::new();
12255                    loop {
12256                        col_list.push(self.expect_identifier_or_keyword_with_quoted()?);
12257                        if !self.match_token(TokenType::Comma) {
12258                            break;
12259                        }
12260                    }
12261                    self.expect(TokenType::RParen)?;
12262                    col_list
12263                } else {
12264                    Vec::new()
12265                };
12266                indices.push(crate::expressions::RollupIndex {
12267                    name,
12268                    expressions: cols,
12269                });
12270                if !self.match_token(TokenType::Comma) {
12271                    break;
12272                }
12273            }
12274            self.expect(TokenType::RParen)?;
12275            Some(crate::expressions::RollupProperty {
12276                expressions: indices,
12277            })
12278        } else {
12279            None
12280        };
12281
12282        // Parse Hive table properties: ROW FORMAT, STORED AS/BY, LOCATION, TBLPROPERTIES
12283        let hive_properties = self.parse_hive_table_properties()?;
12284        let is_teradata = matches!(
12285            self.config.dialect,
12286            Some(crate::dialects::DialectType::Teradata)
12287        );
12288
12289        // Handle ON COMMIT PRESERVE ROWS or ON COMMIT DELETE ROWS
12290        // Also handle TSQL ON filegroup or ON filegroup (partition_column)
12291        let (mut on_commit, on_property) = if is_teradata {
12292            (None, None)
12293        } else if self.match_token(TokenType::On) {
12294            if self.match_token(TokenType::Commit) {
12295                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
12296                    (Some(OnCommit::PreserveRows), None)
12297                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
12298                    (Some(OnCommit::DeleteRows), None)
12299                } else {
12300                    return Err(
12301                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
12302                    );
12303                }
12304            } else {
12305                // TSQL: ON filegroup or ON filegroup (partition_column)
12306                // Parse filegroup name as schema which allows filegroup(column) syntax
12307                let filegroup = self.parse_schema_identifier()?;
12308                (
12309                    None,
12310                    Some(OnProperty {
12311                        this: Box::new(filegroup),
12312                    }),
12313                )
12314            }
12315        } else {
12316            (None, None)
12317        };
12318
12319        // Parse table properties like DEFAULT COLLATE (BigQuery)
12320        let mut table_properties = hive_properties;
12321
12322        // If COMMENT was found before WITH, add it to table_properties as SchemaCommentProperty
12323        if let Some(comment_text) = pre_with_comment {
12324            table_properties.push(Expression::SchemaCommentProperty(Box::new(
12325                SchemaCommentProperty {
12326                    this: Box::new(Expression::Literal(Box::new(Literal::String(comment_text)))),
12327                },
12328            )));
12329        }
12330
12331        if self.match_token(TokenType::Default) && self.match_token(TokenType::Collate) {
12332            let collation = self.parse_primary()?;
12333            table_properties.push(Expression::CollateProperty(Box::new(CollateProperty {
12334                this: Box::new(collation),
12335                default: Some(Box::new(Expression::Boolean(BooleanLiteral {
12336                    value: true,
12337                }))),
12338            })));
12339        }
12340
12341        // BigQuery: OPTIONS (key=value, ...) on table - comes after column definitions
12342        if matches!(
12343            self.config.dialect,
12344            Some(crate::dialects::DialectType::BigQuery)
12345        ) {
12346            if let Some(options_property) = self.parse_bigquery_options_property()? {
12347                table_properties.push(options_property);
12348            }
12349        } else if self.match_identifier("OPTIONS") {
12350            let options = self.parse_options_list()?;
12351            table_properties.push(Expression::Properties(Box::new(Properties {
12352                expressions: options,
12353            })));
12354        }
12355
12356        // Doris/StarRocks: PROPERTIES ('key'='value', ...) - comes after column definitions
12357        let is_doris_starrocks = matches!(
12358            self.config.dialect,
12359            Some(crate::dialects::DialectType::Doris)
12360                | Some(crate::dialects::DialectType::StarRocks)
12361        );
12362        if is_doris_starrocks && self.match_identifier("PROPERTIES") {
12363            // Use parse_options_list which handles 'key'='value' format
12364            let props = self.parse_options_list()?;
12365            if !props.is_empty() {
12366                table_properties.push(Expression::Properties(Box::new(Properties {
12367                    expressions: props,
12368                })));
12369            }
12370        }
12371
12372        // Redshift: Parse DISTKEY, SORTKEY, DISTSTYLE, BACKUP after column definitions
12373        // These can appear in any order and multiple times
12374        loop {
12375            if self.match_identifier("DISTKEY") {
12376                // DISTKEY(column)
12377                if let Some(distkey) = self.parse_distkey()? {
12378                    table_properties.push(distkey);
12379                }
12380            } else if self.match_text_seq(&["COMPOUND", "SORTKEY"]) {
12381                // COMPOUND SORTKEY(col1, col2, ...)
12382                if let Some(sortkey) = self.parse_sortkey()? {
12383                    // Set compound flag
12384                    if let Expression::SortKeyProperty(mut skp) = sortkey {
12385                        skp.compound = Some(Box::new(Expression::Boolean(BooleanLiteral {
12386                            value: true,
12387                        })));
12388                        table_properties.push(Expression::SortKeyProperty(skp));
12389                    }
12390                }
12391            } else if self.match_identifier("SORTKEY") {
12392                // SORTKEY(col1, col2, ...)
12393                if let Some(sortkey) = self.parse_sortkey()? {
12394                    table_properties.push(sortkey);
12395                }
12396            } else if self.match_identifier("DISTSTYLE") {
12397                // DISTSTYLE ALL|EVEN|AUTO|KEY
12398                if self.match_texts(&["ALL", "EVEN", "AUTO", "KEY"]) {
12399                    let style = self.previous().text.to_ascii_uppercase();
12400                    table_properties.push(Expression::DistStyleProperty(Box::new(
12401                        DistStyleProperty {
12402                            this: Box::new(Expression::Var(Box::new(Var { this: style }))),
12403                        },
12404                    )));
12405                }
12406            } else if self.match_identifier("BACKUP") {
12407                // BACKUP YES|NO
12408                if self.match_texts(&["YES", "NO"]) {
12409                    let value = self.previous().text.to_ascii_uppercase();
12410                    table_properties.push(Expression::BackupProperty(Box::new(BackupProperty {
12411                        this: Box::new(Expression::Var(Box::new(Var { this: value }))),
12412                    })));
12413                }
12414            } else {
12415                break;
12416            }
12417        }
12418
12419        // Teradata: PRIMARY/UNIQUE/INDEX and PARTITION BY clauses after columns
12420        if is_teradata {
12421            loop {
12422                // Consume optional comma separator between index specs (only if followed by an index keyword)
12423                if self.check(TokenType::Comma) {
12424                    let saved_comma = self.current;
12425                    self.skip(); // consume comma
12426                    let is_index_keyword = self.check(TokenType::Unique)
12427                        || self.check(TokenType::PrimaryKey)
12428                        || self.check(TokenType::Index)
12429                        || self.check(TokenType::No);
12430                    if !is_index_keyword {
12431                        self.current = saved_comma; // retreat
12432                    }
12433                }
12434                if self.match_token(TokenType::Unique) {
12435                    let primary = self.match_token(TokenType::PrimaryKey);
12436                    let amp = self.match_identifier("AMP");
12437                    self.match_token(TokenType::Index);
12438                    let params = if self.match_token(TokenType::LParen) {
12439                        let cols = self.parse_identifier_list()?;
12440                        self.expect(TokenType::RParen)?;
12441                        cols.into_iter()
12442                            .map(|id| {
12443                                Expression::boxed_column(Column {
12444                                    name: id,
12445                                    table: None,
12446                                    join_mark: false,
12447                                    trailing_comments: Vec::new(),
12448                                    span: None,
12449                                    inferred_type: None,
12450                                })
12451                            })
12452                            .collect()
12453                    } else {
12454                        Vec::new()
12455                    };
12456                    table_properties.push(Expression::Index(Box::new(Index {
12457                        this: None,
12458                        table: None,
12459                        unique: true,
12460                        primary: if primary {
12461                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12462                                value: true,
12463                            })))
12464                        } else {
12465                            None
12466                        },
12467                        amp: if amp {
12468                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12469                                value: true,
12470                            })))
12471                        } else {
12472                            None
12473                        },
12474                        params,
12475                    })));
12476                    continue;
12477                }
12478                if self.match_token(TokenType::PrimaryKey) {
12479                    let amp = self.match_identifier("AMP");
12480                    self.match_token(TokenType::Index);
12481                    let params = if self.match_token(TokenType::LParen) {
12482                        let cols = self.parse_identifier_list()?;
12483                        self.expect(TokenType::RParen)?;
12484                        cols.into_iter()
12485                            .map(|id| {
12486                                Expression::boxed_column(Column {
12487                                    name: id,
12488                                    table: None,
12489                                    join_mark: false,
12490                                    trailing_comments: Vec::new(),
12491                                    span: None,
12492                                    inferred_type: None,
12493                                })
12494                            })
12495                            .collect()
12496                    } else {
12497                        Vec::new()
12498                    };
12499                    table_properties.push(Expression::Index(Box::new(Index {
12500                        this: None,
12501                        table: None,
12502                        unique: false,
12503                        primary: Some(Box::new(Expression::Boolean(BooleanLiteral {
12504                            value: true,
12505                        }))),
12506                        amp: if amp {
12507                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12508                                value: true,
12509                            })))
12510                        } else {
12511                            None
12512                        },
12513                        params,
12514                    })));
12515                    continue;
12516                }
12517                if self.match_token(TokenType::Index) {
12518                    let params = if self.match_token(TokenType::LParen) {
12519                        let cols = self.parse_identifier_list()?;
12520                        self.expect(TokenType::RParen)?;
12521                        cols.into_iter()
12522                            .map(|id| {
12523                                Expression::boxed_column(Column {
12524                                    name: id,
12525                                    table: None,
12526                                    join_mark: false,
12527                                    trailing_comments: Vec::new(),
12528                                    span: None,
12529                                    inferred_type: None,
12530                                })
12531                            })
12532                            .collect()
12533                    } else {
12534                        Vec::new()
12535                    };
12536                    table_properties.push(Expression::Index(Box::new(Index {
12537                        this: None,
12538                        table: None,
12539                        unique: false,
12540                        primary: None,
12541                        amp: None,
12542                        params,
12543                    })));
12544                    continue;
12545                }
12546                if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
12547                    let expr = self.parse_primary()?;
12548                    table_properties.push(Expression::PartitionedByProperty(Box::new(
12549                        PartitionedByProperty {
12550                            this: Box::new(expr),
12551                        },
12552                    )));
12553                    continue;
12554                }
12555                break;
12556            }
12557
12558            if on_commit.is_none()
12559                && self.check(TokenType::On)
12560                && self.check_next(TokenType::Commit)
12561            {
12562                self.skip(); // ON
12563                self.skip(); // COMMIT
12564                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
12565                    on_commit = Some(OnCommit::PreserveRows);
12566                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
12567                    on_commit = Some(OnCommit::DeleteRows);
12568                } else {
12569                    return Err(
12570                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
12571                    );
12572                }
12573            }
12574        }
12575
12576        // ClickHouse: table properties after column definitions
12577        if is_clickhouse {
12578            self.parse_clickhouse_table_properties(&mut table_properties)?;
12579        }
12580
12581        // ClickHouse: EMPTY AS SELECT
12582        if matches!(
12583            self.config.dialect,
12584            Some(crate::dialects::DialectType::ClickHouse)
12585        ) && self.match_identifier("EMPTY")
12586        {
12587            table_properties.push(Expression::Var(Box::new(Var {
12588                this: "EMPTY".to_string(),
12589            })));
12590        }
12591
12592        // Handle AS SELECT after columns/WITH (CTAS with column definitions)
12593        // When there are no column definitions, AS comes after PARTITION BY/CLUSTER BY/OPTIONS
12594        let as_select = if !no_column_defs && self.match_token(TokenType::As) {
12595            Some(self.parse_statement()?)
12596        } else {
12597            None
12598        };
12599
12600        if is_clickhouse && as_select.is_some() {
12601            self.parse_clickhouse_table_properties(&mut table_properties)?;
12602        }
12603
12604        // Parse PARTITION BY RANGE/LIST/HASH(columns) for regular CREATE TABLE
12605        let is_bigquery = matches!(
12606            self.config.dialect,
12607            Some(crate::dialects::DialectType::BigQuery)
12608        );
12609        if !is_teradata && (self.check(TokenType::Partition) || self.check(TokenType::PartitionBy))
12610        {
12611            let parsed_bigquery_partition = if is_bigquery {
12612                if let Some(partition_property) = self.parse_bigquery_partition_by_property()? {
12613                    table_properties.push(partition_property);
12614                    true
12615                } else {
12616                    false
12617                }
12618            } else {
12619                false
12620            };
12621
12622            if !parsed_bigquery_partition {
12623                let saved = self.current;
12624                let is_partition_by = if self.match_token(TokenType::PartitionBy) {
12625                    true
12626                } else if self.match_token(TokenType::Partition) {
12627                    self.match_token(TokenType::By)
12628                } else {
12629                    false
12630                };
12631                if is_partition_by {
12632                    let partition_kind = if self.check(TokenType::Range) {
12633                        self.skip();
12634                        Some("RANGE".to_string())
12635                    } else if self.check(TokenType::List) {
12636                        self.skip();
12637                        Some("LIST".to_string())
12638                    } else if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
12639                        && self.check_next(TokenType::LParen)
12640                    {
12641                        // Only treat identifier as partition method (like HASH) if followed by (
12642                        Some(self.advance().text.to_ascii_uppercase())
12643                    } else {
12644                        // No explicit partition method (RANGE/LIST/HASH), just PARTITION BY (cols)
12645                        None
12646                    };
12647
12648                    // StarRocks/Doris: PARTITION BY func(), col (bare expressions without RANGE/LIST)
12649                    // When the partition_kind was consumed as an identifier that's actually a function call
12650                    // and the content after the parenthesized args includes a comma, it's a bare expression list
12651                    if is_doris_starrocks
12652                        && partition_kind.is_some()
12653                        && !matches!(
12654                            partition_kind.as_deref(),
12655                            Some("RANGE") | Some("LIST") | Some("HASH") | Some("KEY")
12656                        )
12657                    {
12658                        // Backtrack: re-parse as bare PARTITION BY with comma-separated expressions
12659                        let func_name = partition_kind.unwrap();
12660                        let mut raw_sql = format!("PARTITION BY {}", func_name);
12661                        // Helper closure for consuming parenthesized content with proper spacing
12662                        fn consume_parens(parser: &mut Parser, raw_sql: &mut String) {
12663                            if !parser.check(TokenType::LParen) {
12664                                return;
12665                            }
12666                            parser.advance();
12667                            raw_sql.push('(');
12668                            let mut depth = 1;
12669                            let mut last_type: Option<TokenType> = None;
12670                            while !parser.is_at_end() && depth > 0 {
12671                                let tok = parser.advance();
12672                                if tok.token_type == TokenType::LParen {
12673                                    depth += 1;
12674                                } else if tok.token_type == TokenType::RParen {
12675                                    depth -= 1;
12676                                    if depth == 0 {
12677                                        break;
12678                                    }
12679                                }
12680                                // Add space after commas
12681                                if matches!(last_type, Some(TokenType::Comma)) {
12682                                    raw_sql.push(' ');
12683                                }
12684                                if tok.token_type == TokenType::String {
12685                                    raw_sql.push('\'');
12686                                    raw_sql.push_str(&tok.text);
12687                                    raw_sql.push('\'');
12688                                } else {
12689                                    raw_sql.push_str(&tok.text);
12690                                }
12691                                last_type = Some(tok.token_type.clone());
12692                            }
12693                            raw_sql.push(')');
12694                        }
12695                        consume_parens(self, &mut raw_sql);
12696                        // Consume more comma-separated expressions
12697                        while self.match_token(TokenType::Comma) {
12698                            raw_sql.push_str(", ");
12699                            let tok = self.advance();
12700                            raw_sql.push_str(&tok.text);
12701                            consume_parens(self, &mut raw_sql);
12702                        }
12703                        table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
12704                    } else
12705                    // For Doris/StarRocks/MySQL RANGE/LIST, use structured parsing
12706                    if (is_doris_starrocks
12707                        || matches!(
12708                            self.config.dialect,
12709                            Some(crate::dialects::DialectType::MySQL)
12710                                | Some(crate::dialects::DialectType::SingleStore)
12711                                | Some(crate::dialects::DialectType::TiDB)
12712                        ))
12713                        && matches!(partition_kind.as_deref(), Some("RANGE") | Some("LIST"))
12714                    {
12715                        let partition_expr = self.parse_doris_partition_by_range_or_list(
12716                            partition_kind
12717                                .as_ref()
12718                                .map(|s| s.as_str())
12719                                .unwrap_or("RANGE"),
12720                        )?;
12721                        table_properties.push(partition_expr);
12722                    } else {
12723                        // Generic raw SQL parsing for other dialects
12724                        let no_partition_kind = partition_kind.is_none();
12725                        let mut raw_sql = match partition_kind {
12726                            Some(kind) => format!("PARTITION BY {}", kind),
12727                            None => "PARTITION BY ".to_string(),
12728                        };
12729                        if self.check(TokenType::LParen) {
12730                            self.skip();
12731                            raw_sql.push('(');
12732                            let mut depth = 1;
12733                            let mut last_tok_type: Option<TokenType> = None;
12734                            while !self.is_at_end() && depth > 0 {
12735                                let tok = self.advance();
12736                                if tok.token_type == TokenType::LParen {
12737                                    depth += 1;
12738                                } else if tok.token_type == TokenType::RParen {
12739                                    depth -= 1;
12740                                    if depth == 0 {
12741                                        break;
12742                                    }
12743                                }
12744                                // Add space before token if needed for proper formatting
12745                                let needs_space = match (&last_tok_type, &tok.token_type) {
12746                                    // Add space after comma
12747                                    (Some(TokenType::Comma), _) => true,
12748                                    // Add space after identifiers/keywords before other identifiers/keywords
12749                                    (Some(TokenType::Identifier), TokenType::Identifier) => true,
12750                                    _ => false,
12751                                };
12752                                if needs_space {
12753                                    raw_sql.push(' ');
12754                                }
12755                                // Handle string literals - preserve quotes
12756                                if tok.token_type == TokenType::String {
12757                                    raw_sql.push('\'');
12758                                    raw_sql.push_str(&tok.text);
12759                                    raw_sql.push('\'');
12760                                } else {
12761                                    raw_sql.push_str(&tok.text);
12762                                }
12763                                last_tok_type = Some(tok.token_type.clone());
12764                            }
12765                            raw_sql.push(')');
12766                        } else if no_partition_kind {
12767                            // Bare PARTITION BY expression list without a partition method
12768                            let mut first = true;
12769                            while !self.is_at_end()
12770                                && !self.check(TokenType::Cluster)
12771                                && !self.check(TokenType::As)
12772                                && !self.check(TokenType::Semicolon)
12773                                && !self.check(TokenType::RParen)
12774                                && !self.check_identifier("OPTIONS")
12775                            {
12776                                if !first {
12777                                    raw_sql.push_str(", ");
12778                                }
12779                                first = false;
12780                                let tok = self.advance();
12781                                raw_sql.push_str(&tok.text);
12782                                // Handle function calls: PARTITION BY DATE(col)
12783                                if self.check(TokenType::LParen) {
12784                                    self.skip();
12785                                    raw_sql.push('(');
12786                                    let mut depth = 1;
12787                                    while !self.is_at_end() && depth > 0 {
12788                                        let t = self.advance();
12789                                        if t.token_type == TokenType::LParen {
12790                                            depth += 1;
12791                                        } else if t.token_type == TokenType::RParen {
12792                                            depth -= 1;
12793                                            if depth == 0 {
12794                                                break;
12795                                            }
12796                                        }
12797                                        raw_sql.push_str(&t.text);
12798                                    }
12799                                    raw_sql.push(')');
12800                                }
12801                                if !self.match_token(TokenType::Comma) {
12802                                    break;
12803                                }
12804                            }
12805                        }
12806                        table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
12807                    }
12808                } else {
12809                    self.current = saved;
12810                }
12811            }
12812        }
12813
12814        // Parse CLUSTER BY (BigQuery) after PARTITION BY
12815        if is_bigquery {
12816            if let Some(cluster_property) = self.parse_bigquery_cluster_by_property()? {
12817                table_properties.push(cluster_property);
12818            }
12819        } else if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
12820            let mut cluster_names = Vec::new();
12821            loop {
12822                let name = self.expect_identifier_or_keyword()?;
12823                cluster_names.push(name);
12824                if !self.match_token(TokenType::Comma) {
12825                    break;
12826                }
12827            }
12828            table_properties.push(Expression::Raw(Raw {
12829                sql: format!("CLUSTER BY {}", cluster_names.join(", ")),
12830            }));
12831        }
12832
12833        // No-column-defs path: OPTIONS and AS SELECT come after PARTITION BY / CLUSTER BY
12834        if no_column_defs {
12835            if matches!(
12836                self.config.dialect,
12837                Some(crate::dialects::DialectType::BigQuery)
12838            ) {
12839                if let Some(options_property) = self.parse_bigquery_options_property()? {
12840                    table_properties.push(options_property);
12841                }
12842            } else if self.match_identifier("OPTIONS") {
12843                let options = self.parse_options_list()?;
12844                table_properties.push(Expression::Properties(Box::new(Properties {
12845                    expressions: options,
12846                })));
12847            }
12848        }
12849
12850        let as_select = if no_column_defs && self.match_token(TokenType::As) {
12851            Some(self.parse_statement()?)
12852        } else {
12853            as_select
12854        };
12855
12856        // For EXTERNAL tables, parse additional Snowflake options that may come after PARTITION BY
12857        // (location=@s2/logs/, partition_type = user_specified, file_format = (...), etc.)
12858        if is_special_modifier {
12859            while !self.is_at_end()
12860                && !self.check(TokenType::As)
12861                && !self.check(TokenType::Semicolon)
12862            {
12863                let is_snowflake_option = self.check(TokenType::Warehouse)
12864                    || self.check_identifier("TARGET_LAG")
12865                    || self.check_identifier("CATALOG")
12866                    || self.check_identifier("EXTERNAL_VOLUME")
12867                    || self.check_identifier("BASE_LOCATION")
12868                    || self.check_identifier("REFRESH_MODE")
12869                    || self.check_identifier("INITIALIZE")
12870                    || self.check_identifier("DATA_RETENTION_TIME_IN_DAYS")
12871                    || self.check_identifier("LOCATION")
12872                    || self.check_identifier("PARTITION_TYPE")
12873                    || self.check_identifier("FILE_FORMAT")
12874                    || self.check_identifier("AUTO_REFRESH");
12875                if is_snowflake_option {
12876                    let key = self.advance().text;
12877                    if self.match_token(TokenType::Eq) {
12878                        let value = if self.check(TokenType::LParen) {
12879                            // Parenthesized option list
12880                            self.skip();
12881                            let mut options = String::from("(");
12882                            let mut depth = 1;
12883                            while !self.is_at_end() && depth > 0 {
12884                                let tok = self.advance();
12885                                if tok.token_type == TokenType::LParen {
12886                                    depth += 1;
12887                                } else if tok.token_type == TokenType::RParen {
12888                                    depth -= 1;
12889                                }
12890                                if !options.ends_with('(')
12891                                    && !options.ends_with(' ')
12892                                    && tok.token_type != TokenType::RParen
12893                                {
12894                                    options.push(' ');
12895                                }
12896                                options.push_str(&tok.text);
12897                            }
12898                            options
12899                        } else if self.check(TokenType::String) {
12900                            let v = format!("'{}'", self.peek().text);
12901                            self.skip();
12902                            v
12903                        } else if self.check(TokenType::DAt) {
12904                            // Stage path like @s1/logs/
12905                            self.skip();
12906                            let mut path = String::from("@");
12907                            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
12908                                path.push_str(&self.advance().text);
12909                            }
12910                            while self.check(TokenType::Slash) {
12911                                if self.current + 1 < self.tokens.len() {
12912                                    let next = &self.tokens[self.current + 1];
12913                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12914                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12915                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12916                                        || next.text.eq_ignore_ascii_case("LOCATION")
12917                                        || next.text.eq_ignore_ascii_case("PARTITION")
12918                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12919                                    {
12920                                        self.skip();
12921                                        path.push('/');
12922                                        break;
12923                                    }
12924                                }
12925                                self.skip();
12926                                path.push('/');
12927                                if self.is_identifier_token()
12928                                    || self.is_safe_keyword_as_identifier()
12929                                {
12930                                    path.push_str(&self.advance().text);
12931                                }
12932                            }
12933                            path
12934                        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
12935                            let mut path = self.advance().text;
12936                            while self.check(TokenType::Slash) {
12937                                if self.current + 1 < self.tokens.len() {
12938                                    let next = &self.tokens[self.current + 1];
12939                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12940                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12941                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12942                                        || next.text.eq_ignore_ascii_case("LOCATION")
12943                                        || next.text.eq_ignore_ascii_case("PARTITION")
12944                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12945                                    {
12946                                        self.skip();
12947                                        path.push('/');
12948                                        break;
12949                                    }
12950                                }
12951                                self.skip();
12952                                path.push('/');
12953                                if self.is_identifier_token()
12954                                    || self.is_safe_keyword_as_identifier()
12955                                {
12956                                    path.push_str(&self.advance().text);
12957                                }
12958                            }
12959                            path
12960                        } else if self.check(TokenType::Warehouse) {
12961                            self.advance().text
12962                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
12963                        {
12964                            self.advance().text
12965                        } else {
12966                            break;
12967                        };
12968                        all_with_properties.push((key, value));
12969                    } else if self.is_identifier_token()
12970                        || self.is_safe_keyword_as_identifier()
12971                        || self.check(TokenType::Warehouse)
12972                    {
12973                        let value = self.advance().text;
12974                        all_with_properties.push((key, value));
12975                    }
12976                } else {
12977                    break;
12978                }
12979            }
12980        }
12981
12982        // Parse TSQL table-level WITH(SYSTEM_VERSIONING=ON(...)) after columns
12983        // This is different from the earlier WITH properties parsing.
12984        // TSQL uses WITH(...) after columns for system versioning.
12985        let post_table_properties = self.parse_post_table_properties()?;
12986
12987        // PostgreSQL: INHERITS (parent1, parent2, ...)
12988        let inherits = if self.match_identifier("INHERITS") {
12989            self.expect(TokenType::LParen)?;
12990            let mut parents = Vec::new();
12991            loop {
12992                parents.push(self.parse_table_ref()?);
12993                if !self.match_token(TokenType::Comma) {
12994                    break;
12995                }
12996            }
12997            self.expect(TokenType::RParen)?;
12998            parents
12999        } else {
13000            Vec::new()
13001        };
13002
13003        Ok(Expression::CreateTable(Box::new(CreateTable {
13004            name,
13005            on_cluster,
13006            columns,
13007            constraints,
13008            if_not_exists,
13009            temporary,
13010            or_replace,
13011            table_modifier: table_modifier.map(|s| s.to_string()),
13012            as_select,
13013            as_select_parenthesized: false,
13014            on_commit,
13015            clone_source: None,
13016            clone_at_clause: None,
13017            shallow_clone: false,
13018            is_copy: false,
13019            leading_comments,
13020            with_properties: all_with_properties,
13021            teradata_post_name_options: teradata_post_name_options.clone(),
13022            with_data: None,
13023            with_statistics: None,
13024            teradata_indexes: Vec::new(),
13025            with_cte: None,
13026            properties: table_properties,
13027            partition_of: None,
13028            post_table_properties,
13029            mysql_table_options,
13030            inherits,
13031            on_property,
13032            copy_grants,
13033            using_template: None,
13034            rollup,
13035            uuid,
13036        })))
13037    }
13038
13039    /// Parse CREATE TABLE ... PARTITION OF parent_table [(cols)] [FOR VALUES spec | DEFAULT] [PARTITION BY ...]
13040    fn parse_create_table_partition_of(
13041        &mut self,
13042        name: TableRef,
13043        if_not_exists: bool,
13044        temporary: bool,
13045        or_replace: bool,
13046        table_modifier: Option<&str>,
13047        leading_comments: Vec<String>,
13048    ) -> Result<Expression> {
13049        // Parse parent table name
13050        let parent_table = self.parse_table_ref()?;
13051
13052        // Optionally parse column constraints in parens: (unitsales DEFAULT 0) or (CONSTRAINT ...)
13053        // This must come before FOR VALUES or DEFAULT. We distinguish from other uses
13054        // by checking if the first token after LParen is CONSTRAINT or an identifier
13055        // that is not a string literal.
13056        let (columns, constraints) = if self.check(TokenType::LParen) {
13057            // Peek ahead: current is LParen, current+1 is first token inside parens
13058            let first_inside = self.current + 1;
13059            // Check if this is a partition column specification: (colname DEFAULT value)
13060            // Column names tokenize as Var (unquoted) or QuotedIdentifier (quoted)
13061            let is_column_defs = first_inside < self.tokens.len()
13062                && (self.tokens[first_inside].token_type == TokenType::Constraint
13063                    || ((self.tokens[first_inside].token_type == TokenType::Var
13064                        || self.tokens[first_inside].token_type == TokenType::QuotedIdentifier
13065                        || self.tokens[first_inside].token_type == TokenType::Identifier)
13066                        && first_inside + 1 < self.tokens.len()
13067                        && self.tokens[first_inside + 1].token_type == TokenType::Default));
13068
13069            if is_column_defs {
13070                self.skip(); // consume LParen
13071                             // Use special parsing for partition column specs - they don't have data types,
13072                             // just column names with constraint overrides like DEFAULT
13073                let (cols, constrs) = self.parse_partition_column_specs()?;
13074                self.expect(TokenType::RParen)?;
13075                (cols, constrs)
13076            } else {
13077                (Vec::new(), Vec::new())
13078            }
13079        } else {
13080            (Vec::new(), Vec::new())
13081        };
13082
13083        // Parse DEFAULT or FOR VALUES spec
13084        let partition_bound: Expression = if self.match_token(TokenType::Default) {
13085            // DEFAULT partition
13086            Expression::Var(Box::new(Var {
13087                this: "DEFAULT".to_string(),
13088            }))
13089        } else if self.match_token(TokenType::For) {
13090            // FOR VALUES ...
13091            self.expect(TokenType::Values)?;
13092            self.parse_partition_bound_spec()?
13093        } else {
13094            // Neither DEFAULT nor FOR VALUES - could be an error
13095            // but we'll be lenient and just create a DEFAULT
13096            Expression::Var(Box::new(Var {
13097                this: "DEFAULT".to_string(),
13098            }))
13099        };
13100
13101        let partition_of_expr =
13102            Expression::PartitionedOfProperty(Box::new(PartitionedOfProperty {
13103                this: Box::new(Expression::Table(Box::new(parent_table))),
13104                expression: Box::new(partition_bound),
13105            }));
13106
13107        // Optionally parse trailing PARTITION BY RANGE/LIST/HASH(columns)
13108        let mut table_properties: Vec<Expression> = Vec::new();
13109        if self.match_token(TokenType::Partition) || self.match_token(TokenType::PartitionBy) {
13110            // Could be PARTITION BY or just PartitionBy token
13111            if self.previous().token_type == TokenType::Partition {
13112                self.expect(TokenType::By)?;
13113            }
13114            // Parse RANGE/LIST/HASH(columns)
13115            let partition_kind = if self.check(TokenType::Identifier) || self.check(TokenType::Var)
13116            {
13117                let kind_text = self.advance().text.to_ascii_uppercase();
13118                kind_text
13119            } else if self.check(TokenType::Range) {
13120                self.skip();
13121                "RANGE".to_string()
13122            } else if self.check(TokenType::List) {
13123                self.skip();
13124                "LIST".to_string()
13125            } else {
13126                "RANGE".to_string()
13127            };
13128            // Parse (columns)
13129            let mut raw_sql = format!("PARTITION BY {}", partition_kind);
13130            if self.check(TokenType::LParen) {
13131                self.skip(); // consume LParen
13132                raw_sql.push('(');
13133                let mut depth = 1;
13134                while !self.is_at_end() && depth > 0 {
13135                    let tok = self.advance();
13136                    if tok.token_type == TokenType::LParen {
13137                        depth += 1;
13138                    } else if tok.token_type == TokenType::RParen {
13139                        depth -= 1;
13140                        if depth == 0 {
13141                            break;
13142                        }
13143                    }
13144                    raw_sql.push_str(&tok.text);
13145                }
13146                raw_sql.push(')');
13147            }
13148            table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
13149        }
13150
13151        Ok(Expression::CreateTable(Box::new(CreateTable {
13152            name,
13153            on_cluster: None,
13154            columns,
13155            constraints,
13156            if_not_exists,
13157            temporary,
13158            or_replace,
13159            table_modifier: table_modifier.map(|s| s.to_string()),
13160            as_select: None,
13161            as_select_parenthesized: false,
13162            on_commit: None,
13163            clone_source: None,
13164            clone_at_clause: None,
13165            shallow_clone: false,
13166            is_copy: false,
13167            leading_comments,
13168            with_properties: Vec::new(),
13169            teradata_post_name_options: Vec::new(),
13170            with_data: None,
13171            with_statistics: None,
13172            teradata_indexes: Vec::new(),
13173            with_cte: None,
13174            properties: table_properties,
13175            partition_of: Some(partition_of_expr),
13176            post_table_properties: Vec::new(),
13177            mysql_table_options: Vec::new(),
13178            inherits: Vec::new(),
13179            on_property: None,
13180            copy_grants: false,
13181            using_template: None,
13182            rollup: None,
13183            uuid: None,
13184        })))
13185    }
13186
13187    /// Parse partition bound spec for PARTITION OF: IN (...), FROM (...) TO (...), or WITH (MODULUS n, REMAINDER n)
13188    fn parse_partition_bound_spec(&mut self) -> Result<Expression> {
13189        if self.match_token(TokenType::In) {
13190            // IN (val, val, ...)
13191            self.expect(TokenType::LParen)?;
13192            let mut values = Vec::new();
13193            loop {
13194                let val = self.parse_expression()?;
13195                values.push(val);
13196                if !self.match_token(TokenType::Comma) {
13197                    break;
13198                }
13199            }
13200            self.expect(TokenType::RParen)?;
13201            // Use Tuple for multiple values (generator strips parens for partition bounds)
13202            let this_expr = if values.len() == 1 {
13203                values.into_iter().next().unwrap()
13204            } else {
13205                Expression::Tuple(Box::new(Tuple {
13206                    expressions: values,
13207                }))
13208            };
13209            Ok(Expression::PartitionBoundSpec(Box::new(
13210                PartitionBoundSpec {
13211                    this: Some(Box::new(this_expr)),
13212                    expression: None,
13213                    from_expressions: None,
13214                    to_expressions: None,
13215                },
13216            )))
13217        } else if self.match_token(TokenType::From) {
13218            // FROM (val, ...) TO (val, ...)
13219            self.expect(TokenType::LParen)?;
13220            let mut from_vals = Vec::new();
13221            loop {
13222                let val = self.parse_partition_bound_value()?;
13223                from_vals.push(val);
13224                if !self.match_token(TokenType::Comma) {
13225                    break;
13226                }
13227            }
13228            self.expect(TokenType::RParen)?;
13229
13230            self.expect(TokenType::To)?;
13231            self.expect(TokenType::LParen)?;
13232            let mut to_vals = Vec::new();
13233            loop {
13234                let val = self.parse_partition_bound_value()?;
13235                to_vals.push(val);
13236                if !self.match_token(TokenType::Comma) {
13237                    break;
13238                }
13239            }
13240            self.expect(TokenType::RParen)?;
13241
13242            let from_expr = if from_vals.len() == 1 {
13243                from_vals.into_iter().next().unwrap()
13244            } else {
13245                Expression::Tuple(Box::new(Tuple {
13246                    expressions: from_vals,
13247                }))
13248            };
13249            let to_expr = if to_vals.len() == 1 {
13250                to_vals.into_iter().next().unwrap()
13251            } else {
13252                Expression::Tuple(Box::new(Tuple {
13253                    expressions: to_vals,
13254                }))
13255            };
13256
13257            Ok(Expression::PartitionBoundSpec(Box::new(
13258                PartitionBoundSpec {
13259                    this: None,
13260                    expression: None,
13261                    from_expressions: Some(Box::new(from_expr)),
13262                    to_expressions: Some(Box::new(to_expr)),
13263                },
13264            )))
13265        } else if self.match_token(TokenType::With) {
13266            // WITH (MODULUS n, REMAINDER n)
13267            self.expect(TokenType::LParen)?;
13268            self.match_text_seq(&["MODULUS"]);
13269            let modulus = self.parse_expression()?;
13270            self.expect(TokenType::Comma)?;
13271            self.match_text_seq(&["REMAINDER"]);
13272            let remainder = self.parse_expression()?;
13273            self.expect(TokenType::RParen)?;
13274
13275            Ok(Expression::PartitionBoundSpec(Box::new(
13276                PartitionBoundSpec {
13277                    this: Some(Box::new(modulus)),
13278                    expression: Some(Box::new(remainder)),
13279                    from_expressions: None,
13280                    to_expressions: None,
13281                },
13282            )))
13283        } else {
13284            Err(self.parse_error("Expected IN, FROM, or WITH after FOR VALUES in PARTITION OF"))
13285        }
13286    }
13287
13288    /// Parse a single partition bound value (number, string, MINVALUE, MAXVALUE)
13289    fn parse_partition_bound_value(&mut self) -> Result<Expression> {
13290        if self.match_token(TokenType::Minvalue) {
13291            Ok(Expression::Var(Box::new(Var {
13292                this: "MINVALUE".to_string(),
13293            })))
13294        } else if self.match_token(TokenType::Maxvalue) {
13295            Ok(Expression::Var(Box::new(Var {
13296                this: "MAXVALUE".to_string(),
13297            })))
13298        } else {
13299            self.parse_expression()
13300        }
13301    }
13302
13303    /// Parse column specifications for PostgreSQL PARTITION OF syntax.
13304    /// Unlike regular column definitions, these don't have data types - just column names
13305    /// with constraint overrides like DEFAULT, NOT NULL, or table-level CONSTRAINT clauses.
13306    /// Example: (unitsales DEFAULT 0) or (CONSTRAINT check_date CHECK (logdate >= '2016-07-01'))
13307    fn parse_partition_column_specs(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>)> {
13308        let mut columns = Vec::new();
13309        let mut constraints = Vec::new();
13310
13311        loop {
13312            // Check for table-level constraint (CONSTRAINT name ...)
13313            if self.check(TokenType::Constraint) {
13314                constraints.push(self.parse_table_constraint()?);
13315            } else if self.check(TokenType::PrimaryKey)
13316                || self.check(TokenType::ForeignKey)
13317                || self.check(TokenType::Unique)
13318                || self.check(TokenType::Check)
13319                || self.check(TokenType::Exclude)
13320            {
13321                constraints.push(self.parse_table_constraint()?);
13322            } else {
13323                // Parse column name with optional constraints (no data type)
13324                columns.push(self.parse_partition_column_spec()?);
13325            }
13326
13327            if !self.match_token(TokenType::Comma) {
13328                break;
13329            }
13330            // ClickHouse allows a trailing comma before the closing ')'
13331            if matches!(
13332                self.config.dialect,
13333                Some(crate::dialects::DialectType::ClickHouse)
13334            ) && self.check(TokenType::RParen)
13335            {
13336                break;
13337            }
13338        }
13339
13340        Ok((columns, constraints))
13341    }
13342
13343    /// Parse a single partition column specification: column_name [DEFAULT value] [NOT NULL] [NULL] [WITH OPTIONS ...]
13344    fn parse_partition_column_spec(&mut self) -> Result<ColumnDef> {
13345        // Parse column name
13346        let name = self.expect_identifier_or_safe_keyword_with_quoted()?;
13347
13348        // Create column def with Unknown data type (data type comes from parent table)
13349        let mut col_def = ColumnDef::new(name.name.clone(), DataType::Unknown);
13350        col_def.name = name;
13351
13352        // Parse column constraints (no data type expected)
13353        loop {
13354            if self.match_token(TokenType::Default) {
13355                // DEFAULT value
13356                let default_val = self.parse_expression()?;
13357                col_def.default = Some(default_val.clone());
13358                col_def
13359                    .constraints
13360                    .push(ColumnConstraint::Default(default_val));
13361                col_def.constraint_order.push(ConstraintType::Default);
13362            } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13363                col_def.nullable = Some(false);
13364                col_def.constraint_order.push(ConstraintType::NotNull);
13365            } else if self.match_token(TokenType::Null) {
13366                col_def.nullable = Some(true);
13367                col_def.constraint_order.push(ConstraintType::Null);
13368            } else if self.match_token(TokenType::Constraint) {
13369                // Inline CONSTRAINT name ... for this column
13370                let constraint_name = self.expect_identifier_or_safe_keyword()?;
13371                if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13372                    col_def.nullable = Some(false);
13373                    col_def.not_null_constraint_name = Some(constraint_name);
13374                    col_def.constraint_order.push(ConstraintType::NotNull);
13375                } else if self.match_token(TokenType::Check) {
13376                    col_def.check_constraint_name = Some(constraint_name);
13377                    if self.match_token(TokenType::LParen) {
13378                        let check_expr = self.parse_expression()?;
13379                        self.expect(TokenType::RParen)?;
13380                        col_def
13381                            .constraints
13382                            .push(ColumnConstraint::Check(check_expr));
13383                    }
13384                    col_def.constraint_order.push(ConstraintType::Check);
13385                } else if self.match_token(TokenType::Default) {
13386                    let default_val = self.parse_expression()?;
13387                    col_def.default = Some(default_val.clone());
13388                    col_def
13389                        .constraints
13390                        .push(ColumnConstraint::Default(default_val));
13391                    col_def.constraint_order.push(ConstraintType::Default);
13392                }
13393            } else if self.match_text_seq(&["WITH", "OPTIONS"]) {
13394                // PostgreSQL: WITH OPTIONS allows specifying more options
13395                // For now, just skip this - it's rarely used
13396                break;
13397            } else {
13398                break;
13399            }
13400        }
13401
13402        Ok(col_def)
13403    }
13404
13405    /// Parse WITH properties for CREATE TABLE (e.g., WITH (FORMAT='parquet', x='2'))
13406    /// Returns a list of (key, value) pairs
13407    fn parse_with_properties(&mut self) -> Result<Vec<(String, String)>> {
13408        self.expect(TokenType::LParen)?;
13409        let mut properties = Vec::new();
13410
13411        loop {
13412            if self.check(TokenType::RParen) {
13413                break;
13414            }
13415
13416            // Parse property name (can be keywords like FORMAT, TABLE_FORMAT)
13417            let mut key = self.expect_identifier_or_keyword()?;
13418
13419            // Handle multi-word keys like "PARTITIONED BY" -> "PARTITIONED_BY"
13420            if key.eq_ignore_ascii_case("PARTITIONED") && self.check(TokenType::By) {
13421                self.skip(); // consume BY
13422                key = "PARTITIONED_BY".to_string();
13423            }
13424
13425            // Expect = or special case for PARTITIONED_BY=(...)
13426            self.expect(TokenType::Eq)?;
13427
13428            // Parse property value - can be string, identifier, or parenthesized expression
13429            let value = if self.check(TokenType::String) {
13430                // Store string with quotes to preserve format
13431                let val = format!("'{}'", self.peek().text);
13432                self.skip();
13433                val
13434            } else if self.match_token(TokenType::LParen) {
13435                // Handle PARTITIONED_BY=(x INT, y INT) or similar
13436                let mut depth = 1;
13437                let mut result = String::from("(");
13438                let mut need_space = false;
13439                while !self.is_at_end() && depth > 0 {
13440                    if self.check(TokenType::LParen) {
13441                        depth += 1;
13442                    } else if self.check(TokenType::RParen) {
13443                        depth -= 1;
13444                        if depth == 0 {
13445                            break;
13446                        }
13447                    }
13448                    let token = self.peek();
13449                    let text = &token.text;
13450                    let token_type = token.token_type;
13451
13452                    // Determine if we need a space before this token
13453                    let is_punctuation = matches!(
13454                        token_type,
13455                        TokenType::Comma | TokenType::LParen | TokenType::RParen
13456                    );
13457                    if need_space && !is_punctuation {
13458                        result.push(' ');
13459                    }
13460
13461                    result.push_str(text);
13462
13463                    // Determine if we need a space after this token
13464                    need_space = token_type == TokenType::Comma
13465                        || (!is_punctuation
13466                            && !matches!(
13467                                token_type,
13468                                TokenType::LParen | TokenType::RParen | TokenType::Comma
13469                            ));
13470                    self.skip();
13471                }
13472                self.expect(TokenType::RParen)?;
13473                result.push(')');
13474                result
13475            } else if self.check_identifier("ARRAY")
13476                && self
13477                    .peek_nth(1)
13478                    .is_some_and(|t| t.token_type == TokenType::LBracket)
13479            {
13480                // Handle ARRAY['value', 'value', ...] syntax (Athena/Presto)
13481                let mut result = self.advance().text.clone(); // consume ARRAY
13482                self.expect(TokenType::LBracket)?;
13483                result.push('[');
13484                let mut first = true;
13485                while !self.is_at_end() && !self.check(TokenType::RBracket) {
13486                    if !first {
13487                        if self.match_token(TokenType::Comma) {
13488                            result.push_str(", ");
13489                        } else {
13490                            break;
13491                        }
13492                    }
13493                    first = false;
13494                    // Parse array element (usually a string)
13495                    if self.check(TokenType::String) {
13496                        result.push('\'');
13497                        result.push_str(&self.advance().text);
13498                        result.push('\'');
13499                    } else if self.is_identifier_token() {
13500                        result.push_str(&self.advance().text);
13501                    } else {
13502                        break;
13503                    }
13504                }
13505                self.expect(TokenType::RBracket)?;
13506                result.push(']');
13507                result
13508            } else if self.check(TokenType::Number) {
13509                // Numeric value (e.g., bucket_count=64)
13510                self.advance().text.clone()
13511            } else {
13512                // Just an identifier or keyword (e.g., allow_page_locks=on)
13513                self.expect_identifier_or_keyword()?
13514            };
13515
13516            properties.push((key, value));
13517
13518            if !self.match_token(TokenType::Comma) {
13519                break;
13520            }
13521        }
13522
13523        self.expect(TokenType::RParen)?;
13524        Ok(properties)
13525    }
13526
13527    /// Parse column definitions and table constraints
13528    fn parse_column_definitions(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>)> {
13529        let mut columns = Vec::new();
13530        let mut constraints = Vec::new();
13531
13532        loop {
13533            if self.check(TokenType::RParen) {
13534                break;
13535            }
13536            // Check for LIKE clause (PostgreSQL)
13537            if self.check(TokenType::Like) {
13538                constraints.push(self.parse_like_clause()?);
13539            }
13540            // Check for table-level constraint
13541            // For CHECK, only treat as constraint if followed by '(' (NOT in ClickHouse — there
13542            // CHECK/ASSUME without CONSTRAINT keyword is not supported, and 'check' can be a column name).
13543            // Otherwise, 'check' is a column name (e.g., CREATE TABLE t (check INT)).
13544            else if self.check(TokenType::Constraint)
13545                || self.check(TokenType::PrimaryKey)
13546                || self.check(TokenType::ForeignKey)
13547                || self.check(TokenType::Unique)
13548                || (self.check(TokenType::Check)
13549                    && !matches!(
13550                        self.config.dialect,
13551                        Some(crate::dialects::DialectType::ClickHouse)
13552                    )
13553                    && self
13554                        .peek_nth(1)
13555                        .map_or(false, |t| t.token_type == TokenType::LParen))
13556                || self.check(TokenType::Exclude)
13557            {
13558                constraints.push(self.parse_table_constraint()?);
13559            } else if matches!(
13560                self.config.dialect,
13561                Some(crate::dialects::DialectType::ClickHouse)
13562            ) && self.check(TokenType::Index)
13563            {
13564                // ClickHouse: INDEX name expr TYPE type_func(args) GRANULARITY n
13565                self.skip(); // consume INDEX
13566                let name = self.expect_identifier_or_keyword_with_quoted()?;
13567                // Use parse_conjunction to handle comparisons like c0 < (SELECT _table)
13568                let expression = self.parse_conjunction()?.ok_or_else(|| {
13569                    self.parse_error("Expected expression in ClickHouse INDEX definition")
13570                })?;
13571                let index_type = if self.match_token(TokenType::Type) {
13572                    // Parse function or identifier for type (e.g., bloom_filter(0.001), set(100), minmax)
13573                    // Handle keywords like 'set' that are tokenized as TokenType::Set
13574                    if let Some(func) = self.parse_function()? {
13575                        Some(Box::new(func))
13576                    } else if !self.check(TokenType::Identifier)
13577                        && !self.check(TokenType::Var)
13578                        && !self.is_at_end()
13579                    {
13580                        // Handle keywords as index type names (e.g., set, minmax)
13581                        let type_name = self.advance().text.clone();
13582                        if self.check(TokenType::LParen) {
13583                            // It's a function call like set(100)
13584                            self.skip(); // consume (
13585                            let mut args = Vec::new();
13586                            if !self.check(TokenType::RParen) {
13587                                args.push(self.parse_expression()?);
13588                                while self.match_token(TokenType::Comma) {
13589                                    args.push(self.parse_expression()?);
13590                                }
13591                            }
13592                            self.expect(TokenType::RParen)?;
13593                            Some(Box::new(Expression::Function(Box::new(Function::new(
13594                                type_name, args,
13595                            )))))
13596                        } else {
13597                            // Just an identifier
13598                            Some(Box::new(Expression::Identifier(Identifier::new(type_name))))
13599                        }
13600                    } else if let Some(id) = self.parse_id_var()? {
13601                        Some(Box::new(id))
13602                    } else {
13603                        None
13604                    }
13605                } else {
13606                    None
13607                };
13608                let granularity = if self.match_identifier("GRANULARITY") {
13609                    let gran_val = self.parse_expression()?;
13610                    Some(Box::new(gran_val))
13611                } else {
13612                    None
13613                };
13614                constraints.push(TableConstraint::Index {
13615                    name: Some(name),
13616                    columns: Vec::new(),
13617                    kind: None,
13618                    modifiers: ConstraintModifiers::default(),
13619                    use_key_keyword: false,
13620                    expression: Some(Box::new(expression)),
13621                    index_type,
13622                    granularity,
13623                });
13624            } else if !matches!(
13625                self.config.dialect,
13626                Some(crate::dialects::DialectType::ClickHouse)
13627            ) && (self.check(TokenType::Index)
13628                || self.check(TokenType::Key)
13629                || self.check_identifier("FULLTEXT")
13630                || self.check_identifier("SPATIAL"))
13631            {
13632                // INDEX/KEY constraint (MySQL). Guard KEY <type> as a normal column definition
13633                // (e.g. ClickHouse: `key UInt64`).
13634                let looks_like_key_constraint = if self.check(TokenType::Key) {
13635                    self.check_next(TokenType::LParen)
13636                        || ((self.check_next(TokenType::Identifier)
13637                            || self.check_next(TokenType::Var)
13638                            || self.check_next(TokenType::QuotedIdentifier))
13639                            && self.current + 2 < self.tokens.len()
13640                            && self.tokens[self.current + 2].token_type == TokenType::LParen)
13641                } else {
13642                    true
13643                };
13644
13645                if looks_like_key_constraint {
13646                    constraints.push(self.parse_index_table_constraint()?);
13647                } else {
13648                    columns.push(self.parse_column_def()?);
13649                }
13650            } else if self.check_identifier("PERIOD") {
13651                // TSQL: PERIOD FOR SYSTEM_TIME (start_col, end_col)
13652                if let Some(period_constraint) =
13653                    self.parse_period_for_system_time_table_constraint()?
13654                {
13655                    constraints.push(period_constraint);
13656                } else {
13657                    // Not actually PERIOD FOR SYSTEM_TIME, treat as column definition
13658                    columns.push(self.parse_column_def()?);
13659                }
13660            } else if self.check_identifier("INITIALLY") {
13661                // PostgreSQL: INITIALLY DEFERRED / INITIALLY IMMEDIATE as table-level setting
13662                self.skip(); // consume INITIALLY
13663                if self.match_identifier("DEFERRED") {
13664                    constraints.push(TableConstraint::InitiallyDeferred { deferred: true });
13665                } else if self.match_identifier("IMMEDIATE") {
13666                    constraints.push(TableConstraint::InitiallyDeferred { deferred: false });
13667                } else {
13668                    return Err(self.parse_error("Expected DEFERRED or IMMEDIATE after INITIALLY"));
13669                }
13670            } else if matches!(
13671                self.config.dialect,
13672                Some(crate::dialects::DialectType::ClickHouse)
13673            ) && self.check_identifier("PROJECTION")
13674            {
13675                // ClickHouse: PROJECTION name (SELECT ...) or PROJECTION name INDEX expr TYPE type_name
13676                self.skip(); // consume PROJECTION
13677                let name = self.expect_identifier_or_keyword_with_quoted()?;
13678                if self.match_token(TokenType::LParen) {
13679                    let expression = self.parse_statement()?;
13680                    self.expect(TokenType::RParen)?;
13681                    // ClickHouse: PROJECTION name (SELECT ...) WITH SETTINGS (key=value, ...)
13682                    if self.check(TokenType::With)
13683                        && self.current + 1 < self.tokens.len()
13684                        && self.tokens[self.current + 1].token_type == TokenType::Settings
13685                    {
13686                        self.skip(); // consume WITH
13687                        self.skip(); // consume SETTINGS
13688                        if self.match_token(TokenType::LParen) {
13689                            // Consume key=value pairs
13690                            loop {
13691                                if self.check(TokenType::RParen) {
13692                                    break;
13693                                }
13694                                if self.is_identifier_token()
13695                                    || self.is_safe_keyword_as_identifier()
13696                                {
13697                                    self.skip(); // key
13698                                }
13699                                if self.match_token(TokenType::Eq) {
13700                                    let _ = self.parse_primary()?; // value
13701                                }
13702                                if !self.match_token(TokenType::Comma) {
13703                                    break;
13704                                }
13705                            }
13706                            self.expect(TokenType::RParen)?;
13707                        }
13708                    }
13709                    constraints.push(TableConstraint::Projection { name, expression });
13710                } else if self.match_token(TokenType::Index) {
13711                    // PROJECTION name INDEX expr TYPE type_name
13712                    let expr = self.parse_bitwise()?.ok_or_else(|| {
13713                        self.parse_error(
13714                            "Expected expression in ClickHouse PROJECTION INDEX definition",
13715                        )
13716                    })?;
13717                    let type_str = if self.match_token(TokenType::Type) {
13718                        if !self.is_at_end()
13719                            && !self.check(TokenType::Comma)
13720                            && !self.check(TokenType::RParen)
13721                        {
13722                            self.advance().text.clone()
13723                        } else {
13724                            String::new()
13725                        }
13726                    } else {
13727                        String::new()
13728                    };
13729                    let raw_sql = if type_str.is_empty() {
13730                        format!("INDEX {} ", expr)
13731                    } else {
13732                        format!("INDEX {} TYPE {}", expr, type_str)
13733                    };
13734                    constraints.push(TableConstraint::Projection {
13735                        name,
13736                        expression: Expression::Raw(Raw { sql: raw_sql }),
13737                    });
13738                } else {
13739                    constraints.push(TableConstraint::Projection {
13740                        name,
13741                        expression: Expression::Null(Null),
13742                    });
13743                }
13744            } else {
13745                // Parse column definition
13746                columns.push(self.parse_column_def()?);
13747            }
13748
13749            if !self.match_token(TokenType::Comma) {
13750                break;
13751            }
13752            // ClickHouse: allow trailing comma before closing paren
13753            if matches!(
13754                self.config.dialect,
13755                Some(crate::dialects::DialectType::ClickHouse)
13756            ) && self.check(TokenType::RParen)
13757            {
13758                break;
13759            }
13760        }
13761
13762        Ok((columns, constraints))
13763    }
13764
13765    /// Parse LIKE clause in CREATE TABLE: LIKE source_table [INCLUDING|EXCLUDING options]
13766    fn parse_like_clause(&mut self) -> Result<TableConstraint> {
13767        self.expect(TokenType::Like)?;
13768        let source = self.parse_table_ref()?;
13769        let mut options = Vec::new();
13770
13771        // Parse optional INCLUDING/EXCLUDING modifiers
13772        loop {
13773            if self.match_identifier("INCLUDING") {
13774                let prop = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
13775                options.push((LikeOptionAction::Including, prop));
13776            } else if self.match_identifier("EXCLUDING") {
13777                let prop = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
13778                options.push((LikeOptionAction::Excluding, prop));
13779            } else {
13780                break;
13781            }
13782        }
13783
13784        Ok(TableConstraint::Like { source, options })
13785    }
13786
13787    /// Parse a single column definition
13788    fn parse_column_def(&mut self) -> Result<ColumnDef> {
13789        // Column names can be keywords like 'end', 'truncate', 'view', etc.
13790        // ClickHouse allows any keyword as column name (from, select, etc.)
13791        let mut name = if matches!(
13792            self.config.dialect,
13793            Some(crate::dialects::DialectType::ClickHouse)
13794        ) {
13795            self.expect_identifier_or_keyword_with_quoted()?
13796        } else {
13797            self.expect_identifier_or_safe_keyword_with_quoted()?
13798        };
13799        // ClickHouse: Nested column names like n.b for Nested() columns
13800        if matches!(
13801            self.config.dialect,
13802            Some(crate::dialects::DialectType::ClickHouse)
13803        ) {
13804            while self.match_token(TokenType::Dot) {
13805                let sub = self.expect_identifier_or_safe_keyword_with_quoted()?;
13806                name = Identifier {
13807                    name: format!("{}.{}", name.name, sub.name),
13808                    quoted: name.quoted,
13809                    trailing_comments: sub.trailing_comments,
13810                    span: None,
13811                };
13812            }
13813        }
13814
13815        // TSQL computed columns have no data type: column_name AS (expression) [PERSISTED]
13816        // Check if AS follows immediately (no data type)
13817        if self.check(TokenType::As) {
13818            let mut col_def = ColumnDef::new(
13819                name.name.clone(),
13820                DataType::Custom {
13821                    name: String::new(),
13822                },
13823            );
13824            col_def.name = name;
13825            // Consume AS and parse computed column expression
13826            self.skip(); // consume AS
13827            if self.check(TokenType::LParen) {
13828                self.parse_as_computed_column(&mut col_def)?;
13829            }
13830            return Ok(col_def);
13831        }
13832
13833        // SQLite allows column definitions without types: CREATE TABLE t (x, y)
13834        // ClickHouse allows typeless columns with DEFAULT/MATERIALIZED/ALIAS/EPHEMERAL
13835        // Check if the next token indicates no type (comma, rparen, or constraint keyword)
13836        let no_type = self.check(TokenType::Comma)
13837            || self.check(TokenType::RParen)
13838            || (matches!(
13839                self.config.dialect,
13840                Some(crate::dialects::DialectType::ClickHouse)
13841            ) && (self.check(TokenType::Default)
13842                || self.check(TokenType::Materialized)
13843                || self.check_identifier("ALIAS")
13844                || self.check_identifier("EPHEMERAL")));
13845        let data_type = if no_type {
13846            // No type specified - use empty custom type
13847            DataType::Custom {
13848                name: String::new(),
13849            }
13850        } else {
13851            self.parse_data_type()?
13852        };
13853
13854        let mut col_def = ColumnDef::new(name.name.clone(), data_type);
13855        col_def.name = name;
13856        col_def.no_type = no_type;
13857
13858        // Parse MySQL type modifiers (UNSIGNED, ZEROFILL)
13859        // These come after the data type but before other constraints
13860        while self.match_identifier("UNSIGNED")
13861            || self.match_identifier("ZEROFILL")
13862            || self.match_identifier("SIGNED")
13863        {
13864            let modifier = self.previous().text.to_ascii_uppercase();
13865            if modifier == "UNSIGNED" {
13866                col_def.unsigned = true;
13867            } else if modifier == "ZEROFILL" {
13868                col_def.zerofill = true;
13869            }
13870            // SIGNED is the default, no action needed
13871        }
13872
13873        // BigQuery: OPTIONS (key=value, ...) on column - comes right after type
13874        if self.match_identifier("OPTIONS") {
13875            col_def.options = self.parse_options_list()?;
13876        }
13877
13878        // Parse column constraints
13879        loop {
13880            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13881                col_def.nullable = Some(false);
13882                col_def.constraint_order.push(ConstraintType::NotNull);
13883            } else if self.match_token(TokenType::Null) {
13884                col_def.nullable = Some(true);
13885                col_def.constraint_order.push(ConstraintType::Null);
13886            } else if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
13887                // Handle PRIMARY KEY [ASC|DESC]
13888                col_def.primary_key = true;
13889                // Capture ASC/DESC after PRIMARY KEY
13890                if self.match_token(TokenType::Asc) {
13891                    col_def.primary_key_order = Some(SortOrder::Asc);
13892                } else if self.match_token(TokenType::Desc) {
13893                    col_def.primary_key_order = Some(SortOrder::Desc);
13894                }
13895                col_def.constraint_order.push(ConstraintType::PrimaryKey);
13896            } else if self.match_token(TokenType::Constraint) {
13897                // Inline CONSTRAINT name ... (e.g., CONSTRAINT fk_name REFERENCES ...)
13898                let constraint_name = self.expect_identifier()?;
13899                // After constraint name, expect REFERENCES, PRIMARY KEY, UNIQUE, CHECK, NOT NULL, NULL, etc.
13900                if self.match_token(TokenType::References) {
13901                    let mut fk_ref = self.parse_foreign_key_ref()?;
13902                    fk_ref.constraint_name = Some(constraint_name);
13903                    col_def
13904                        .constraints
13905                        .push(ColumnConstraint::References(fk_ref));
13906                    col_def.constraint_order.push(ConstraintType::References);
13907                } else if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
13908                    col_def.primary_key = true;
13909                    col_def.primary_key_constraint_name = Some(constraint_name);
13910                    col_def.constraint_order.push(ConstraintType::PrimaryKey);
13911                } else if self.match_token(TokenType::Unique) {
13912                    col_def.unique = true;
13913                    col_def.unique_constraint_name = Some(constraint_name);
13914                    // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
13915                    if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
13916                        col_def.unique_nulls_not_distinct = true;
13917                    }
13918                    col_def.constraint_order.push(ConstraintType::Unique);
13919                } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13920                    col_def.nullable = Some(false);
13921                    col_def.not_null_constraint_name = Some(constraint_name);
13922                    col_def.constraint_order.push(ConstraintType::NotNull);
13923                } else if self.match_token(TokenType::Check) {
13924                    col_def.check_constraint_name = Some(constraint_name);
13925                    // Parse CHECK constraint expression
13926                    if self.match_token(TokenType::LParen) {
13927                        let check_expr = self.parse_expression()?;
13928                        self.expect(TokenType::RParen)?;
13929                        col_def
13930                            .constraints
13931                            .push(ColumnConstraint::Check(check_expr));
13932                    } else if matches!(
13933                        self.config.dialect,
13934                        Some(crate::dialects::DialectType::ClickHouse)
13935                    ) {
13936                        // ClickHouse: CHECK expr without parens
13937                        let check_expr = self.parse_or()?;
13938                        col_def
13939                            .constraints
13940                            .push(ColumnConstraint::Check(check_expr));
13941                    }
13942                    col_def.constraint_order.push(ConstraintType::Check);
13943                }
13944            } else if self.match_token(TokenType::Unique) {
13945                col_def.unique = true;
13946                // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
13947                if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
13948                    col_def.unique_nulls_not_distinct = true;
13949                }
13950                col_def.constraint_order.push(ConstraintType::Unique);
13951            } else if self.match_token(TokenType::Check) {
13952                // Standalone CHECK (expr) constraint (without CONSTRAINT name)
13953                if self.match_token(TokenType::LParen) {
13954                    let check_expr = self.parse_expression()?;
13955                    self.expect(TokenType::RParen)?;
13956                    col_def
13957                        .constraints
13958                        .push(ColumnConstraint::Check(check_expr));
13959                    col_def.constraint_order.push(ConstraintType::Check);
13960                } else if matches!(
13961                    self.config.dialect,
13962                    Some(crate::dialects::DialectType::ClickHouse)
13963                ) {
13964                    // ClickHouse: CHECK expr without parens
13965                    let check_expr = self.parse_or()?;
13966                    col_def
13967                        .constraints
13968                        .push(ColumnConstraint::Check(check_expr));
13969                    col_def.constraint_order.push(ConstraintType::Check);
13970                }
13971            } else if self.match_token(TokenType::AutoIncrement) || self.match_keyword("IDENTITY") {
13972                col_def.auto_increment = true;
13973                col_def.constraint_order.push(ConstraintType::AutoIncrement);
13974                // Handle IDENTITY/AUTOINCREMENT options: START n INCREMENT m [ORDER|NOORDER] or (start, increment)
13975                if self.match_keyword("START") {
13976                    col_def.auto_increment_start = Some(Box::new(self.parse_primary()?));
13977                    if self.match_keyword("INCREMENT") {
13978                        col_def.auto_increment_increment = Some(Box::new(self.parse_primary()?));
13979                    }
13980                    // Snowflake: ORDER or NOORDER option
13981                    if self.match_token(TokenType::Order) {
13982                        col_def.auto_increment_order = Some(true);
13983                    } else if self.match_identifier("NOORDER") {
13984                        col_def.auto_increment_order = Some(false);
13985                    }
13986                } else if self.match_token(TokenType::LParen) {
13987                    // IDENTITY(start, increment) or AUTOINCREMENT(start, increment)
13988                    col_def.auto_increment_start = Some(Box::new(self.parse_primary()?));
13989                    if self.match_token(TokenType::Comma) {
13990                        col_def.auto_increment_increment = Some(Box::new(self.parse_primary()?));
13991                    }
13992                    self.expect(TokenType::RParen)?;
13993                }
13994            } else if self.match_token(TokenType::Default) {
13995                // ClickHouse: DEFAULT expressions can be complex (today(), a + 1, cond ? x : y, etc.)
13996                col_def.default = if matches!(
13997                    self.config.dialect,
13998                    Some(crate::dialects::DialectType::ClickHouse)
13999                ) {
14000                    Some(self.parse_expression()?)
14001                } else {
14002                    Some(self.parse_unary()?)
14003                };
14004                col_def.constraint_order.push(ConstraintType::Default);
14005            } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
14006                // Snowflake/SQL Server: FOREIGN KEY REFERENCES table(columns)
14007                // The FOREIGN KEY keywords are followed by REFERENCES
14008                self.expect(TokenType::References)?;
14009                let mut fk_ref = self.parse_foreign_key_ref()?;
14010                fk_ref.has_foreign_key_keywords = true;
14011                col_def
14012                    .constraints
14013                    .push(ColumnConstraint::References(fk_ref));
14014                col_def.constraint_order.push(ConstraintType::References);
14015            } else if self.match_token(TokenType::References) {
14016                let fk_ref = self.parse_foreign_key_ref()?;
14017                col_def
14018                    .constraints
14019                    .push(ColumnConstraint::References(fk_ref));
14020                col_def.constraint_order.push(ConstraintType::References);
14021            } else if self.match_token(TokenType::Generated) {
14022                // GENERATED [BY DEFAULT [ON NULL] | ALWAYS] AS ...
14023                // Could be: AS IDENTITY, AS (expr) STORED|VIRTUAL, AS ROW START|END
14024                self.parse_generated_column_constraint(&mut col_def)?;
14025            } else if self.match_token(TokenType::Collate) {
14026                // COLLATE collation_name (may be quoted like "de_DE")
14027                // Also handle dotted names like pg_catalog."default"
14028                let mut collation = self.expect_identifier_or_keyword_with_quoted()?;
14029                // Check for dotted collation names: pg_catalog."default"
14030                while self.match_token(TokenType::Dot) {
14031                    let next = self.expect_identifier_or_keyword_with_quoted()?;
14032                    let sep = if next.quoted {
14033                        format!("{}.\"{}\"", collation.name, next.name)
14034                    } else {
14035                        format!("{}.{}", collation.name, next.name)
14036                    };
14037                    collation = Identifier {
14038                        name: sep,
14039                        quoted: false,
14040                        trailing_comments: Vec::new(),
14041                        span: None,
14042                    };
14043                }
14044                col_def
14045                    .constraints
14046                    .push(ColumnConstraint::Collate(collation));
14047                col_def.constraint_order.push(ConstraintType::Collate);
14048            } else if self.match_token(TokenType::Comment) {
14049                // COMMENT 'comment text'
14050                let comment_text = self.expect_string()?;
14051                col_def
14052                    .constraints
14053                    .push(ColumnConstraint::Comment(comment_text));
14054                col_def.constraint_order.push(ConstraintType::Comment);
14055            } else if self.match_keywords(&[TokenType::On, TokenType::Update]) {
14056                // MySQL: ON UPDATE expression (e.g., ON UPDATE CURRENT_TIMESTAMP)
14057                let expr = self.parse_unary()?;
14058                col_def.on_update = Some(expr);
14059                col_def.constraint_order.push(ConstraintType::OnUpdate);
14060            } else if self.match_identifier("ENCODE") {
14061                // Redshift: ENCODE encoding_type (e.g., ZSTD, DELTA, LZO, etc.)
14062                let encoding = self.expect_identifier_or_keyword()?;
14063                col_def.encoding = Some(encoding);
14064                col_def.constraint_order.push(ConstraintType::Encode);
14065            } else if !matches!(
14066                self.config.dialect,
14067                Some(crate::dialects::DialectType::ClickHouse)
14068            ) && self.match_token(TokenType::Format)
14069            {
14070                // Teradata: FORMAT 'pattern' (not ClickHouse — FORMAT there is statement-level)
14071                let format_str = self.expect_string()?;
14072                col_def.format = Some(format_str);
14073            } else if self.match_identifier("TITLE") {
14074                // Teradata: TITLE 'title'
14075                let title_str = self.expect_string()?;
14076                col_def.title = Some(title_str);
14077            } else if self.match_identifier("INLINE") {
14078                // Teradata: INLINE LENGTH n
14079                self.match_identifier("LENGTH");
14080                let length = self.expect_number()?;
14081                col_def.inline_length = Some(length as u64);
14082            } else if self.match_identifier("COMPRESS") {
14083                // Teradata: COMPRESS or COMPRESS (values) or COMPRESS 'value'
14084                if self.match_token(TokenType::LParen) {
14085                    let values = self.parse_expression_list()?;
14086                    self.expect(TokenType::RParen)?;
14087                    col_def.compress = Some(values);
14088                } else if self.check(TokenType::String) {
14089                    // COMPRESS 'value'
14090                    let value = self.parse_primary()?;
14091                    col_def.compress = Some(vec![value]);
14092                } else {
14093                    // COMPRESS without values
14094                    col_def.compress = Some(Vec::new());
14095                }
14096            } else if self.match_identifier("CHARACTER") {
14097                // Teradata: CHARACTER SET name
14098                self.match_token(TokenType::Set);
14099                let charset = self.expect_identifier_or_keyword()?;
14100                col_def.character_set = Some(charset);
14101            } else if self.match_identifier("UPPERCASE") {
14102                // Teradata: UPPERCASE
14103                col_def.uppercase = true;
14104            } else if self.match_identifier("CASESPECIFIC") {
14105                // Teradata: CASESPECIFIC
14106                col_def.casespecific = Some(true);
14107            } else if self.match_text_seq(&["NOT", "FOR", "REPLICATION"]) {
14108                // TSQL: NOT FOR REPLICATION - skip this modifier (not preserved in output for non-TSQL)
14109                col_def.not_for_replication = true;
14110            } else if self.match_token(TokenType::Not) && self.match_identifier("CASESPECIFIC") {
14111                // Teradata: NOT CASESPECIFIC
14112                col_def.casespecific = Some(false);
14113            } else if self.match_keyword("TAG")
14114                || (self.match_token(TokenType::With) && self.match_keyword("TAG"))
14115            {
14116                // Snowflake: TAG (key='value', ...) or WITH TAG (key='value', ...)
14117                let tags = self.parse_tags()?;
14118                col_def.constraints.push(ColumnConstraint::Tags(tags));
14119                col_def.constraint_order.push(ConstraintType::Tags);
14120            } else if self.match_token(TokenType::As) {
14121                // Computed column: AS (expression) [STORED|VIRTUAL|PERSISTED] [NOT NULL]
14122                // TSQL: AS (expression) [PERSISTED] [NOT NULL]
14123                // MySQL shorthand: AS (expression) [STORED|VIRTUAL]
14124                // Also: Snowflake External Table virtual column expression
14125                if self.check(TokenType::LParen) {
14126                    self.parse_as_computed_column(&mut col_def)?;
14127                }
14128            } else if self.match_identifier("CODEC") {
14129                // ClickHouse: CODEC(LZ4HC(9), ZSTD, DELTA)
14130                self.expect(TokenType::LParen)?;
14131                let start = self.current;
14132                let mut depth = 1;
14133                while !self.is_at_end() && depth > 0 {
14134                    if self.check(TokenType::LParen) {
14135                        depth += 1;
14136                    }
14137                    if self.check(TokenType::RParen) {
14138                        depth -= 1;
14139                        if depth == 0 {
14140                            break;
14141                        }
14142                    }
14143                    self.skip();
14144                }
14145                let codec_text = self.tokens_to_sql(start, self.current);
14146                self.expect(TokenType::RParen)?;
14147                col_def.codec = Some(codec_text);
14148            } else if self.match_identifier("STATISTICS") {
14149                // ClickHouse: STATISTICS(tdigest, minmax, uniq, ...)
14150                self.expect(TokenType::LParen)?;
14151                let mut depth = 1;
14152                while !self.is_at_end() && depth > 0 {
14153                    if self.check(TokenType::LParen) {
14154                        depth += 1;
14155                    }
14156                    if self.check(TokenType::RParen) {
14157                        depth -= 1;
14158                        if depth == 0 {
14159                            break;
14160                        }
14161                    }
14162                    self.skip();
14163                }
14164                self.expect(TokenType::RParen)?;
14165                // Statistics info is stored but we don't need it for transpilation
14166            } else if self.match_identifier("EPHEMERAL") {
14167                // ClickHouse: EPHEMERAL [expr] [type]
14168                // EPHEMERAL can optionally be followed by an expression, then optionally a data type
14169                if !self.check(TokenType::Comma)
14170                    && !self.check(TokenType::RParen)
14171                    && !self.is_at_end()
14172                    && !self.check_identifier("CODEC")
14173                    && !self.check_identifier("TTL")
14174                    && !self.check(TokenType::Comment)
14175                {
14176                    let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
14177                    col_def.ephemeral = Some(Some(Box::new(expr)));
14178                    // ClickHouse: type can follow EPHEMERAL expression (e.g., b EPHEMERAL 'a' String)
14179                    if col_def.no_type
14180                        && !self.check(TokenType::Comma)
14181                        && !self.check(TokenType::RParen)
14182                        && !self.is_at_end()
14183                        && !self.check_identifier("CODEC")
14184                        && !self.check_identifier("TTL")
14185                        && !self.check(TokenType::Comment)
14186                    {
14187                        col_def.data_type = self.parse_data_type()?;
14188                        col_def.no_type = false;
14189                    }
14190                } else {
14191                    col_def.ephemeral = Some(None);
14192                }
14193            } else if self.check(TokenType::Materialized) && !self.check_next(TokenType::View) {
14194                // ClickHouse: MATERIALIZED expr (but not MATERIALIZED VIEW)
14195                self.skip(); // consume MATERIALIZED
14196                let expr = self.parse_or()?;
14197                col_def.materialized_expr = Some(Box::new(expr));
14198            } else if self.match_identifier("ALIAS") {
14199                // ClickHouse: ALIAS expr
14200                let expr = self.parse_or()?;
14201                col_def.alias_expr = Some(Box::new(expr));
14202            } else if matches!(
14203                self.config.dialect,
14204                Some(crate::dialects::DialectType::ClickHouse)
14205            ) && self.check_identifier("EXPRESSION")
14206            {
14207                // ClickHouse dictionary column: EXPRESSION expr
14208                self.skip(); // consume EXPRESSION
14209                let expr = self.parse_or()?;
14210                col_def.materialized_expr = Some(Box::new(expr));
14211            } else if matches!(
14212                self.config.dialect,
14213                Some(crate::dialects::DialectType::ClickHouse)
14214            ) && (self.match_identifier("HIERARCHICAL")
14215                || self.match_identifier("IS_OBJECT_ID")
14216                || self.match_identifier("INJECTIVE")
14217                || self.match_identifier("BIDIRECTIONAL"))
14218            {
14219                // ClickHouse dictionary column attributes: HIERARCHICAL, IS_OBJECT_ID, INJECTIVE, BIDIRECTIONAL
14220                // These are flag-like attributes with no value, just skip them
14221            } else if self.match_identifier("TTL") {
14222                // ClickHouse: TTL expr
14223                let expr = self.parse_expression()?;
14224                col_def.ttl_expr = Some(Box::new(expr));
14225            } else if matches!(
14226                self.config.dialect,
14227                Some(crate::dialects::DialectType::ClickHouse)
14228            ) && self.check(TokenType::Settings)
14229                && self.check_next(TokenType::LParen)
14230            {
14231                // ClickHouse: SETTINGS (key = value, ...) on column definition
14232                // Only match parenthesized form; non-parenthesized SETTINGS is statement-level
14233                self.skip(); // consume SETTINGS
14234                self.expect(TokenType::LParen)?;
14235                let mut depth = 1i32;
14236                while !self.is_at_end() && depth > 0 {
14237                    if self.check(TokenType::LParen) {
14238                        depth += 1;
14239                    }
14240                    if self.check(TokenType::RParen) {
14241                        depth -= 1;
14242                        if depth == 0 {
14243                            break;
14244                        }
14245                    }
14246                    self.skip();
14247                }
14248                self.expect(TokenType::RParen)?;
14249            } else {
14250                // Skip unknown column modifiers (DEFERRABLE, CHARACTER SET, etc.)
14251                // to allow parsing to continue
14252                if self.skip_column_modifier() {
14253                    continue;
14254                }
14255                break;
14256            }
14257        }
14258
14259        Ok(col_def)
14260    }
14261
14262    /// Skip optional column modifiers that we don't need to preserve
14263    fn skip_column_modifier(&mut self) -> bool {
14264        // NOT DEFERRABLE, NOT CASESPECIFIC - handle NOT followed by specific keywords
14265        // (NOT NULL is handled earlier in the constraint loop)
14266        if self.check(TokenType::Not) {
14267            // Check what follows NOT
14268            if self.check_next_identifier("DEFERRABLE")
14269                || self.check_next_identifier("CASESPECIFIC")
14270            {
14271                self.skip(); // consume NOT
14272                self.skip(); // consume DEFERRABLE/CASESPECIFIC
14273                return true;
14274            }
14275        }
14276        // DEFERRABLE / NOT DEFERRABLE / INITIALLY DEFERRED / INITIALLY IMMEDIATE
14277        if self.match_identifier("DEFERRABLE")
14278            || self.match_identifier("DEFERRED")
14279            || self.match_identifier("IMMEDIATE")
14280        {
14281            return true;
14282        }
14283        // CHARACTER SET name
14284        if self.match_identifier("CHARACTER") {
14285            self.match_token(TokenType::Set);
14286            // Consume charset name (can be multiple parts like LATIN, utf8_bin, etc.)
14287            let _ = self.match_token(TokenType::Var) || self.match_token(TokenType::Identifier);
14288            return true;
14289        }
14290        // UPPERCASE, CASESPECIFIC
14291        if self.match_identifier("UPPERCASE") || self.match_identifier("CASESPECIFIC") {
14292            return true;
14293        }
14294        // Note: COMPRESS, FORMAT, TITLE, and INLINE LENGTH are now properly parsed and stored in ColumnDef
14295        false
14296    }
14297
14298    /// Parse Teradata-specific table options after CREATE TABLE AS
14299    /// Returns (with_data, with_statistics, teradata_indexes)
14300    fn parse_teradata_table_options(&mut self) -> (Option<bool>, Option<bool>, Vec<TeradataIndex>) {
14301        let mut with_data = None;
14302        let mut with_statistics = None;
14303        let mut teradata_indexes = Vec::new();
14304
14305        loop {
14306            // WITH DATA [AND STATISTICS] / WITH NO DATA [AND NO STATISTICS]
14307            if self.match_token(TokenType::With) {
14308                let no = self.match_token(TokenType::No); // optional NO
14309                self.match_identifier("DATA");
14310                with_data = Some(!no); // WITH DATA = true, WITH NO DATA = false
14311                                       // Optional AND [NO] STATISTICS
14312                if self.match_token(TokenType::And) {
14313                    let no_stats = self.match_token(TokenType::No); // optional NO
14314                    self.match_identifier("STATISTICS");
14315                    with_statistics = Some(!no_stats); // AND STATISTICS = true, AND NO STATISTICS = false
14316                }
14317                continue;
14318            }
14319            // NO PRIMARY INDEX
14320            if self.match_token(TokenType::No) {
14321                self.match_token(TokenType::PrimaryKey);
14322                self.match_token(TokenType::Index);
14323                teradata_indexes.push(TeradataIndex {
14324                    kind: TeradataIndexKind::NoPrimary,
14325                    name: None,
14326                    columns: Vec::new(),
14327                });
14328                // Consume optional comma separator between index specs
14329                self.match_token(TokenType::Comma);
14330                continue;
14331            }
14332            // PRIMARY AMP INDEX / PRIMARY INDEX
14333            if self.match_token(TokenType::PrimaryKey) {
14334                let is_amp = self.match_identifier("AMP");
14335                self.match_token(TokenType::Index);
14336                // Optional index name
14337                let name = if self.is_identifier_token() && !self.check(TokenType::LParen) {
14338                    Some(self.advance().text)
14339                } else {
14340                    None
14341                };
14342                // Optional column list
14343                let columns = if self.match_token(TokenType::LParen) {
14344                    let cols = self.parse_identifier_list_raw();
14345                    self.match_token(TokenType::RParen);
14346                    cols
14347                } else {
14348                    Vec::new()
14349                };
14350                teradata_indexes.push(TeradataIndex {
14351                    kind: if is_amp {
14352                        TeradataIndexKind::PrimaryAmp
14353                    } else {
14354                        TeradataIndexKind::Primary
14355                    },
14356                    name,
14357                    columns,
14358                });
14359                // Consume optional comma separator between index specs
14360                self.match_token(TokenType::Comma);
14361                continue;
14362            }
14363            // UNIQUE [PRIMARY] INDEX
14364            if self.match_token(TokenType::Unique) {
14365                let is_primary = self.match_token(TokenType::PrimaryKey);
14366                self.match_token(TokenType::Index);
14367                // Optional index name
14368                let name = if self.is_identifier_token() {
14369                    Some(self.advance().text)
14370                } else {
14371                    None
14372                };
14373                // Optional column list
14374                let columns = if self.match_token(TokenType::LParen) {
14375                    let cols = self.parse_identifier_list_raw();
14376                    self.match_token(TokenType::RParen);
14377                    cols
14378                } else {
14379                    Vec::new()
14380                };
14381                teradata_indexes.push(TeradataIndex {
14382                    kind: if is_primary {
14383                        TeradataIndexKind::UniquePrimary
14384                    } else {
14385                        TeradataIndexKind::Unique
14386                    },
14387                    name,
14388                    columns,
14389                });
14390                // Consume optional comma separator between index specs
14391                self.match_token(TokenType::Comma);
14392                continue;
14393            }
14394            // Plain INDEX (non-primary, non-unique)
14395            if self.match_token(TokenType::Index) {
14396                // Optional index name
14397                let name = if self.is_identifier_token() && !self.check(TokenType::LParen) {
14398                    Some(self.advance().text)
14399                } else {
14400                    None
14401                };
14402                // Optional column list
14403                let columns = if self.match_token(TokenType::LParen) {
14404                    let cols = self.parse_identifier_list_raw();
14405                    self.match_token(TokenType::RParen);
14406                    cols
14407                } else {
14408                    Vec::new()
14409                };
14410                teradata_indexes.push(TeradataIndex {
14411                    kind: TeradataIndexKind::Secondary,
14412                    name,
14413                    columns,
14414                });
14415                // Consume optional comma separator between index specs
14416                self.match_token(TokenType::Comma);
14417                continue;
14418            }
14419            break;
14420        }
14421
14422        (with_data, with_statistics, teradata_indexes)
14423    }
14424
14425    /// Parse Teradata table options after name before column list (comma-separated)
14426    fn parse_teradata_post_name_options(&mut self) -> Vec<String> {
14427        // Options begin with a comma after the table name.
14428        if !self.match_token(TokenType::Comma) {
14429            return Vec::new();
14430        }
14431
14432        let mut options = Vec::new();
14433        let mut current_tokens: Vec<(String, TokenType)> = Vec::new();
14434        let mut paren_depth = 0;
14435        let mut in_value = false;
14436
14437        while !self.is_at_end() {
14438            if self.check(TokenType::LParen) && paren_depth == 0 {
14439                if !in_value {
14440                    // Column list begins
14441                    break;
14442                }
14443                let mut is_terminal = false;
14444                if let Some((last_text, last_type)) = current_tokens.last() {
14445                    let last_upper = last_text.to_ascii_uppercase();
14446                    is_terminal = matches!(last_type, TokenType::Number | TokenType::String)
14447                        || matches!(
14448                            last_upper.as_str(),
14449                            "ON" | "OFF"
14450                                | "DEFAULT"
14451                                | "NEVER"
14452                                | "ALWAYS"
14453                                | "MINIMUM"
14454                                | "MAXIMUM"
14455                                | "BYTES"
14456                                | "KBYTES"
14457                                | "KILOBYTES"
14458                                | "PERCENT"
14459                        );
14460                }
14461                if is_terminal {
14462                    break;
14463                }
14464            }
14465
14466            let token = self.advance();
14467
14468            match token.token_type {
14469                TokenType::LParen => {
14470                    paren_depth += 1;
14471                }
14472                TokenType::RParen => {
14473                    if paren_depth > 0 {
14474                        paren_depth -= 1;
14475                        if paren_depth == 0 && in_value {
14476                            in_value = false;
14477                        }
14478                    }
14479                }
14480                TokenType::Eq => {
14481                    if paren_depth == 0 {
14482                        in_value = true;
14483                    }
14484                }
14485                TokenType::Comma => {
14486                    if paren_depth == 0 {
14487                        let option = self.join_teradata_option_tokens(current_tokens);
14488                        if !option.is_empty() {
14489                            options.push(option);
14490                        }
14491                        current_tokens = Vec::new();
14492                        in_value = false;
14493                        continue;
14494                    }
14495                }
14496                _ => {}
14497            }
14498
14499            let text = if token.token_type == TokenType::QuotedIdentifier {
14500                let quote_char = if self.config.dialect == Some(crate::dialects::DialectType::MySQL)
14501                    || self.config.dialect == Some(crate::dialects::DialectType::SingleStore)
14502                    || self.config.dialect == Some(crate::dialects::DialectType::Doris)
14503                    || self.config.dialect == Some(crate::dialects::DialectType::StarRocks)
14504                {
14505                    '`'
14506                } else {
14507                    '"'
14508                };
14509                format!("{}{}{}", quote_char, token.text, quote_char)
14510            } else if token.token_type == TokenType::String {
14511                format!("'{}'", token.text)
14512            } else {
14513                token.text.clone()
14514            };
14515
14516            let mut join_type = token.token_type;
14517            if join_type == TokenType::Percent && token.text.eq_ignore_ascii_case("PERCENT") {
14518                // Treat PERCENT as an identifier to preserve spacing (e.g., "1 PERCENT")
14519                join_type = TokenType::Identifier;
14520            }
14521            current_tokens.push((text, join_type));
14522        }
14523
14524        if !current_tokens.is_empty() {
14525            let option = self.join_teradata_option_tokens(current_tokens);
14526            if !option.is_empty() {
14527                options.push(option);
14528            }
14529        }
14530
14531        options
14532    }
14533
14534    /// Parse identifier list for Teradata indexes, returning raw strings
14535    fn parse_identifier_list_raw(&mut self) -> Vec<String> {
14536        let mut identifiers = Vec::new();
14537        loop {
14538            if self.is_identifier_token() || self.is_identifier_or_keyword_token() {
14539                identifiers.push(self.advance().text);
14540            }
14541            if !self.match_token(TokenType::Comma) {
14542                break;
14543            }
14544        }
14545        identifiers
14546    }
14547
14548    /// Parse GENERATED column constraint after GENERATED token has been consumed.
14549    /// Handles three forms:
14550    /// 1. GENERATED [BY DEFAULT | ALWAYS] AS IDENTITY [...] -> GeneratedAsIdentity
14551    /// 2. GENERATED ALWAYS AS (expr) [STORED|VIRTUAL] -> ComputedColumn
14552    /// 3. GENERATED ALWAYS AS ROW START|END [HIDDEN] -> GeneratedAsRow
14553    fn parse_generated_column_constraint(&mut self, col_def: &mut ColumnDef) -> Result<()> {
14554        let always;
14555        let mut on_null = false;
14556
14557        // BY DEFAULT [ON NULL] | ALWAYS
14558        if self.match_token(TokenType::By) {
14559            self.expect(TokenType::Default)?;
14560            on_null = self.match_keywords(&[TokenType::On, TokenType::Null]);
14561            always = false;
14562        } else {
14563            self.expect(TokenType::Always)?;
14564            always = true;
14565        }
14566
14567        // Expect AS
14568        self.expect(TokenType::As)?;
14569
14570        // Check what follows AS
14571        if self.check(TokenType::Row) {
14572            // GENERATED ALWAYS AS ROW START|END [HIDDEN]
14573            self.skip(); // consume ROW
14574            let start = if self.match_token(TokenType::Start) {
14575                true
14576            } else {
14577                self.expect(TokenType::End)?;
14578                false
14579            };
14580            let hidden = self.match_identifier("HIDDEN");
14581            col_def
14582                .constraints
14583                .push(ColumnConstraint::GeneratedAsRow(GeneratedAsRow {
14584                    start,
14585                    hidden,
14586                }));
14587            col_def
14588                .constraint_order
14589                .push(ConstraintType::GeneratedAsRow);
14590        } else if self.check(TokenType::Identity) {
14591            // GENERATED [BY DEFAULT | ALWAYS] AS IDENTITY [(...)]
14592            self.skip(); // consume IDENTITY
14593
14594            let mut start = None;
14595            let mut increment = None;
14596            let mut minvalue = None;
14597            let mut maxvalue = None;
14598            let mut cycle = None;
14599
14600            // Optional sequence options in parentheses
14601            if self.match_token(TokenType::LParen) {
14602                loop {
14603                    if self.match_token(TokenType::Start) {
14604                        self.match_token(TokenType::With);
14605                        start = Some(Box::new(self.parse_unary()?));
14606                    } else if self.match_token(TokenType::Increment) {
14607                        self.match_token(TokenType::By);
14608                        increment = Some(Box::new(self.parse_unary()?));
14609                    } else if self.match_token(TokenType::Minvalue) {
14610                        minvalue = Some(Box::new(self.parse_unary()?));
14611                    } else if self.match_token(TokenType::Maxvalue) {
14612                        maxvalue = Some(Box::new(self.parse_unary()?));
14613                    } else if self.match_token(TokenType::Cycle) {
14614                        cycle = Some(true);
14615                    } else if self.match_keywords(&[TokenType::No, TokenType::Cycle]) {
14616                        cycle = Some(false);
14617                    } else if self.check(TokenType::RParen) {
14618                        break;
14619                    } else {
14620                        self.skip();
14621                    }
14622                }
14623                self.expect(TokenType::RParen)?;
14624            }
14625
14626            col_def
14627                .constraints
14628                .push(ColumnConstraint::GeneratedAsIdentity(GeneratedAsIdentity {
14629                    always,
14630                    on_null,
14631                    start,
14632                    increment,
14633                    minvalue,
14634                    maxvalue,
14635                    cycle,
14636                }));
14637            col_def
14638                .constraint_order
14639                .push(ConstraintType::GeneratedAsIdentity);
14640        } else if self.check(TokenType::LParen) {
14641            // GENERATED ALWAYS AS (expr) [STORED|VIRTUAL]
14642            self.skip(); // consume LParen
14643            let expr = self.parse_expression()?;
14644            self.expect(TokenType::RParen)?;
14645
14646            // Check for STORED or VIRTUAL
14647            let (persisted, persistence_kind) = if self.match_identifier("STORED") {
14648                (true, Some("STORED".to_string()))
14649            } else if self.match_identifier("VIRTUAL") {
14650                (false, Some("VIRTUAL".to_string()))
14651            } else {
14652                (false, None)
14653            };
14654
14655            col_def
14656                .constraints
14657                .push(ColumnConstraint::ComputedColumn(ComputedColumn {
14658                    expression: Box::new(expr),
14659                    persisted,
14660                    not_null: false,
14661                    persistence_kind,
14662                    data_type: None,
14663                }));
14664            col_def
14665                .constraint_order
14666                .push(ConstraintType::ComputedColumn);
14667        } else {
14668            // Fallback: treat as GENERATED AS IDENTITY without explicit IDENTITY keyword
14669            col_def
14670                .constraints
14671                .push(ColumnConstraint::GeneratedAsIdentity(GeneratedAsIdentity {
14672                    always,
14673                    on_null,
14674                    start: None,
14675                    increment: None,
14676                    minvalue: None,
14677                    maxvalue: None,
14678                    cycle: None,
14679                }));
14680            col_def
14681                .constraint_order
14682                .push(ConstraintType::GeneratedAsIdentity);
14683        }
14684        Ok(())
14685    }
14686
14687    /// Parse AS (expr) [STORED|VIRTUAL|PERSISTED] [TYPE] [NOT NULL] for computed columns.
14688    /// Called after AS token has been consumed and we've confirmed LParen follows.
14689    /// SingleStore: AS (expr) PERSISTED TYPE NOT NULL
14690    fn parse_as_computed_column(&mut self, col_def: &mut ColumnDef) -> Result<()> {
14691        self.expect(TokenType::LParen)?;
14692        let expr = self.parse_expression()?;
14693        self.expect(TokenType::RParen)?;
14694
14695        // Check for STORED, VIRTUAL, or PERSISTED
14696        let (persisted, persistence_kind) = if self.match_identifier("STORED") {
14697            (true, Some("STORED".to_string()))
14698        } else if self.match_identifier("VIRTUAL") {
14699            (false, Some("VIRTUAL".to_string()))
14700        } else if self.match_identifier("PERSISTED") {
14701            (true, Some("PERSISTED".to_string()))
14702        } else {
14703            (false, None)
14704        };
14705
14706        // For PERSISTED columns, check for optional data type (SingleStore: PERSISTED TYPE NOT NULL)
14707        // Also check for AUTO keyword for SingleStore: PERSISTED AUTO NOT NULL
14708        let data_type = if persistence_kind.as_deref() == Some("PERSISTED") {
14709            // Check if next token looks like a data type (not NOT, not end of input, not comma/rparen)
14710            if !self.is_at_end()
14711                && !self.check(TokenType::Not)
14712                && !self.check(TokenType::Comma)
14713                && !self.check(TokenType::RParen)
14714                && !self.check(TokenType::Semicolon)
14715            {
14716                let tok = self.peek();
14717                // Check for AUTO keyword (SingleStore: PERSISTED AUTO)
14718                if tok.text.eq_ignore_ascii_case("AUTO") {
14719                    self.skip(); // consume AUTO
14720                    None // AUTO is not a data type, just a modifier
14721                } else if tok.token_type.is_keyword()
14722                    || tok.token_type == TokenType::Identifier
14723                    || tok.token_type == TokenType::Var
14724                {
14725                    Some(self.parse_data_type()?)
14726                } else {
14727                    None
14728                }
14729            } else {
14730                None
14731            }
14732        } else {
14733            None
14734        };
14735
14736        // For PERSISTED columns, check for NOT NULL
14737        let not_null = if persistence_kind.as_deref() == Some("PERSISTED") {
14738            self.match_keywords(&[TokenType::Not, TokenType::Null])
14739        } else {
14740            false
14741        };
14742
14743        col_def
14744            .constraints
14745            .push(ColumnConstraint::ComputedColumn(ComputedColumn {
14746                expression: Box::new(expr),
14747                persisted,
14748                not_null,
14749                persistence_kind,
14750                data_type,
14751            }));
14752        col_def
14753            .constraint_order
14754            .push(ConstraintType::ComputedColumn);
14755        Ok(())
14756    }
14757
14758    /// Parse PERIOD FOR SYSTEM_TIME (start_col, end_col) as a table constraint.
14759    /// Returns None if this is not actually PERIOD FOR SYSTEM_TIME (e.g., just a column named PERIOD).
14760    fn parse_period_for_system_time_table_constraint(&mut self) -> Result<Option<TableConstraint>> {
14761        // Save position for possible retreat
14762        let saved = self.current;
14763
14764        if self.match_identifier("PERIOD") {
14765            // Check if followed by FOR SYSTEM_TIME
14766            if self.match_token(TokenType::For) {
14767                if self.match_identifier("SYSTEM_TIME") {
14768                    // Parse (start_col, end_col)
14769                    self.expect(TokenType::LParen)?;
14770                    let start_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
14771                    self.expect(TokenType::Comma)?;
14772                    let end_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
14773                    self.expect(TokenType::RParen)?;
14774                    return Ok(Some(TableConstraint::PeriodForSystemTime {
14775                        start_col: start_name,
14776                        end_col: end_name,
14777                    }));
14778                }
14779            }
14780        }
14781
14782        // Not PERIOD FOR SYSTEM_TIME, retreat
14783        self.current = saved;
14784        Ok(None)
14785    }
14786
14787    /// Parse MySQL table options that appear after the closing paren of column definitions.
14788    /// Handles ENGINE=val, AUTO_INCREMENT=val, DEFAULT CHARSET=val, ROW_FORMAT=val,
14789    /// COMMENT='val', COLLATE=val, etc.
14790    fn parse_mysql_table_options(&mut self) -> Vec<(String, String)> {
14791        let mut options = Vec::new();
14792        loop {
14793            // Skip optional commas between options
14794            self.match_token(TokenType::Comma);
14795
14796            // DEFAULT CHARSET=val or DEFAULT CHARACTER SET=val
14797            if self.check(TokenType::Default) {
14798                let saved = self.current;
14799                self.skip(); // consume DEFAULT
14800                if self.check_identifier("CHARSET") || self.check_identifier("CHARACTER") {
14801                    let is_character = self.check_identifier("CHARACTER");
14802                    let key_part = self.advance().text.to_ascii_uppercase();
14803                    if is_character {
14804                        // CHARACTER SET
14805                        self.match_token(TokenType::Set);
14806                    }
14807                    if self.match_token(TokenType::Eq) {
14808                        let value = if self.check(TokenType::String) {
14809                            let v = format!("'{}'", self.peek().text);
14810                            self.skip();
14811                            v
14812                        } else if self.is_identifier_token()
14813                            || self.is_safe_keyword_as_identifier()
14814                            || self.check(TokenType::Number)
14815                        {
14816                            self.advance().text
14817                        } else {
14818                            self.current = saved;
14819                            break;
14820                        };
14821                        // Normalize CHARSET -> CHARACTER SET
14822                        let key = if is_character || key_part == "CHARSET" {
14823                            "DEFAULT CHARACTER SET".to_string()
14824                        } else {
14825                            format!("DEFAULT {}", key_part)
14826                        };
14827                        options.push((key, value));
14828                        continue;
14829                    }
14830                }
14831                self.current = saved;
14832                break;
14833            }
14834
14835            // ENGINE=val, AUTO_INCREMENT=val, ROW_FORMAT=val, COLLATE=val, KEY_BLOCK_SIZE=val
14836            let is_known_option = self.check_identifier("ENGINE")
14837                || self.check(TokenType::AutoIncrement)
14838                || self.check_identifier("ROW_FORMAT")
14839                || self.check(TokenType::Collate)
14840                || self.check_identifier("KEY_BLOCK_SIZE")
14841                || self.check_identifier("PACK_KEYS")
14842                || self.check_identifier("STATS_AUTO_RECALC")
14843                || self.check_identifier("STATS_PERSISTENT")
14844                || self.check_identifier("STATS_SAMPLE_PAGES")
14845                || self.check_identifier("MAX_ROWS")
14846                || self.check_identifier("MIN_ROWS")
14847                || self.check_identifier("CHECKSUM")
14848                || self.check_identifier("DELAY_KEY_WRITE")
14849                || self.check_identifier("COMPRESSION")
14850                || self.check_identifier("CONNECTION")
14851                || self.check_identifier("TABLESPACE")
14852                || self.check_identifier("ENCRYPTION");
14853
14854            if is_known_option {
14855                let key = self.advance().text.to_ascii_uppercase();
14856                if self.match_token(TokenType::Eq) {
14857                    let value = if self.check(TokenType::String) {
14858                        let v = format!("'{}'", self.peek().text);
14859                        self.skip();
14860                        v
14861                    } else if self.check(TokenType::Number) {
14862                        self.advance().text
14863                    } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
14864                        self.advance().text
14865                    } else {
14866                        break;
14867                    };
14868                    options.push((key, value));
14869                    continue;
14870                }
14871                break;
14872            }
14873
14874            // COMMENT='val' (Comment is a keyword token type)
14875            if self.check(TokenType::Comment) {
14876                let saved = self.current;
14877                self.skip(); // consume COMMENT
14878                if self.match_token(TokenType::Eq) {
14879                    if self.check(TokenType::String) {
14880                        let v = format!("'{}'", self.peek().text);
14881                        self.skip();
14882                        options.push(("COMMENT".to_string(), v));
14883                        continue;
14884                    }
14885                } else if self.check(TokenType::String) {
14886                    let v = format!("'{}'", self.peek().text);
14887                    self.skip();
14888                    options.push(("COMMENT".to_string(), v));
14889                    continue;
14890                }
14891                self.current = saved;
14892                break;
14893            }
14894
14895            // CHARACTER SET=val or CHARSET=val (without DEFAULT prefix)
14896            if self.check_identifier("CHARACTER") || self.check_identifier("CHARSET") {
14897                let saved = self.current;
14898                let is_character = self.check_identifier("CHARACTER");
14899                self.skip(); // consume CHARACTER or CHARSET
14900                if is_character {
14901                    // CHARACTER SET
14902                    if !self.match_token(TokenType::Set) {
14903                        self.current = saved;
14904                        break;
14905                    }
14906                }
14907                if self.match_token(TokenType::Eq) {
14908                    let value = if self.check(TokenType::String) {
14909                        let v = format!("'{}'", self.peek().text);
14910                        self.skip();
14911                        v
14912                    } else if self.is_identifier_token()
14913                        || self.is_safe_keyword_as_identifier()
14914                        || self.check(TokenType::Number)
14915                    {
14916                        self.advance().text
14917                    } else {
14918                        self.current = saved;
14919                        break;
14920                    };
14921                    options.push(("CHARACTER SET".to_string(), value));
14922                    continue;
14923                }
14924                self.current = saved;
14925                break;
14926            }
14927
14928            break;
14929        }
14930        options
14931    }
14932
14933    /// Parse Hive-specific table properties that appear after column definitions.
14934    /// Handles: ROW FORMAT (SERDE/DELIMITED), STORED AS/BY, LOCATION, TBLPROPERTIES
14935    fn parse_hive_table_properties(&mut self) -> Result<Vec<Expression>> {
14936        let mut properties = Vec::new();
14937
14938        loop {
14939            // ROW FORMAT SERDE 'class' [WITH SERDEPROPERTIES (...)]
14940            // ROW FORMAT DELIMITED [FIELDS TERMINATED BY ...] [...]
14941            if self.match_token(TokenType::Row) {
14942                if let Some(row_format) = self.parse_row()? {
14943                    properties.push(row_format);
14944                    continue;
14945                }
14946            }
14947
14948            // STORED AS INPUTFORMAT 'input' OUTPUTFORMAT 'output'
14949            // STORED AS format_name
14950            // STORED BY 'storage_handler_class'
14951            if self.match_identifier("STORED") {
14952                if self.match_token(TokenType::By) {
14953                    // STORED BY 'storage_handler_class'
14954                    let handler = self.parse_string()?.unwrap_or(Expression::Null(Null));
14955                    properties.push(Expression::StorageHandlerProperty(Box::new(
14956                        StorageHandlerProperty {
14957                            this: Box::new(handler),
14958                        },
14959                    )));
14960                    continue;
14961                } else if self.match_token(TokenType::As) {
14962                    // STORED AS INPUTFORMAT 'x' OUTPUTFORMAT 'y' or STORED AS format
14963                    if self.match_token(TokenType::InputFormat) {
14964                        let input_format = self.parse_string()?;
14965                        let output_format = if self.match_identifier("OUTPUTFORMAT") {
14966                            self.parse_string()?
14967                        } else {
14968                            None
14969                        };
14970                        // Use InputOutputFormat inside FileFormatProperty.this
14971                        let io_format =
14972                            Expression::InputOutputFormat(Box::new(InputOutputFormat {
14973                                input_format: input_format.map(Box::new),
14974                                output_format: output_format.map(Box::new),
14975                            }));
14976                        properties.push(Expression::FileFormatProperty(Box::new(
14977                            FileFormatProperty {
14978                                this: Some(Box::new(io_format)),
14979                                expressions: vec![],
14980                                hive_format: Some(Box::new(Expression::Boolean(BooleanLiteral {
14981                                    value: true,
14982                                }))),
14983                            },
14984                        )));
14985                        continue;
14986                    } else {
14987                        // STORED AS format_name (e.g., STORED AS TEXTFILE, STORED AS ORC)
14988                        let format = if self.check(TokenType::String) {
14989                            Expression::Literal(Box::new(Literal::String(
14990                                self.advance().text.clone(),
14991                            )))
14992                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
14993                        {
14994                            Expression::Identifier(Identifier::new(self.advance().text.clone()))
14995                        } else {
14996                            break;
14997                        };
14998                        properties.push(Expression::FileFormatProperty(Box::new(
14999                            FileFormatProperty {
15000                                this: Some(Box::new(format)),
15001                                expressions: vec![],
15002                                hive_format: Some(Box::new(Expression::Boolean(BooleanLiteral {
15003                                    value: true,
15004                                }))),
15005                            },
15006                        )));
15007                        continue;
15008                    }
15009                }
15010            }
15011
15012            // USING format_name (Databricks/Spark) e.g., USING DELTA, USING PARQUET
15013            // This is similar to STORED AS but uses different syntax
15014            if self.match_token(TokenType::Using) {
15015                // Parse the format name (e.g., DELTA, PARQUET, ICEBERG, etc.)
15016                let format = if self.check(TokenType::String) {
15017                    Expression::Literal(Box::new(Literal::String(self.advance().text.clone())))
15018                } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
15019                    Expression::Identifier(Identifier::new(self.advance().text.clone()))
15020                } else {
15021                    break;
15022                };
15023                // Create FileFormatProperty WITHOUT hive_format to signal USING syntax
15024                properties.push(Expression::FileFormatProperty(Box::new(
15025                    FileFormatProperty {
15026                        this: Some(Box::new(format)),
15027                        expressions: vec![],
15028                        hive_format: None, // None indicates USING syntax (not STORED AS)
15029                    },
15030                )));
15031                continue;
15032            }
15033
15034            // LOCATION 'path'
15035            if self.match_identifier("LOCATION") {
15036                let path = self.parse_string()?.unwrap_or(Expression::Null(Null));
15037                properties.push(Expression::LocationProperty(Box::new(LocationProperty {
15038                    this: Box::new(path),
15039                })));
15040                continue;
15041            }
15042
15043            // TBLPROPERTIES ('key'='value', ...)
15044            if self.match_identifier("TBLPROPERTIES") {
15045                // Parse the property list manually since parse_property doesn't handle key=value
15046                self.expect(TokenType::LParen)?;
15047                let mut prop_exprs = Vec::new();
15048                loop {
15049                    if self.check(TokenType::RParen) {
15050                        break;
15051                    }
15052                    // Parse 'key'='value' or key=value
15053                    let key = self.parse_primary()?;
15054                    if self.match_token(TokenType::Eq) {
15055                        let value = self.parse_primary()?;
15056                        prop_exprs.push(Expression::Eq(Box::new(BinaryOp::new(key, value))));
15057                    } else {
15058                        prop_exprs.push(key);
15059                    }
15060                    if !self.match_token(TokenType::Comma) {
15061                        break;
15062                    }
15063                }
15064                self.expect(TokenType::RParen)?;
15065                properties.push(Expression::Properties(Box::new(Properties {
15066                    expressions: prop_exprs,
15067                })));
15068                continue;
15069            }
15070
15071            // DISTRIBUTED BY HASH (col1, col2) [BUCKETS n] (StarRocks/Doris)
15072            if self.match_identifier("DISTRIBUTED") {
15073                if let Some(dist_prop) = self.parse_distributed_property()? {
15074                    properties.push(dist_prop);
15075                    continue;
15076                }
15077            }
15078
15079            // CLUSTERED BY (col, col, ...) [SORTED BY (col, col, ...)] INTO n BUCKETS (Hive/Athena)
15080            if self.match_identifier("CLUSTERED") {
15081                self.expect(TokenType::By)?;
15082                self.expect(TokenType::LParen)?;
15083                let expressions = self.parse_expression_list()?;
15084                self.expect(TokenType::RParen)?;
15085
15086                // Optional SORTED BY (col, col, ...)
15087                let sorted_by = if self.match_identifier("SORTED") {
15088                    self.expect(TokenType::By)?;
15089                    self.expect(TokenType::LParen)?;
15090                    let sorted_exprs = self.parse_expression_list()?;
15091                    self.expect(TokenType::RParen)?;
15092                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
15093                        expressions: sorted_exprs,
15094                    }))))
15095                } else {
15096                    None
15097                };
15098
15099                // INTO n BUCKETS
15100                let buckets = if self.match_token(TokenType::Into) {
15101                    let num = self.parse_expression()?;
15102                    if !self.match_identifier("BUCKETS") {
15103                        return Err(self.parse_error("Expected BUCKETS after INTO <n>"));
15104                    }
15105                    Some(Box::new(num))
15106                } else {
15107                    None
15108                };
15109
15110                properties.push(Expression::ClusteredByProperty(Box::new(
15111                    ClusteredByProperty {
15112                        expressions,
15113                        sorted_by,
15114                        buckets,
15115                    },
15116                )));
15117                continue;
15118            }
15119
15120            // PARTITIONED BY (col, col, ...) or PARTITIONED BY (col, BUCKET(n, col), ...) (Hive/Athena/Iceberg)
15121            if self.match_identifier("PARTITIONED") {
15122                self.expect(TokenType::By)?;
15123                self.expect(TokenType::LParen)?;
15124
15125                let mut partition_exprs = Vec::new();
15126                loop {
15127                    if self.check(TokenType::RParen) {
15128                        break;
15129                    }
15130
15131                    // Check for transform functions like BUCKET(n, col), TRUNCATE(n, col), etc.
15132                    if self.check_identifier("BUCKET") || self.check_identifier("TRUNCATE") {
15133                        let func_name = self.advance().text.clone();
15134                        self.expect(TokenType::LParen)?;
15135                        let args = self.parse_expression_list()?;
15136                        self.expect(TokenType::RParen)?;
15137
15138                        // Create a Function expression for BUCKET/TRUNCATE
15139                        partition_exprs.push(Expression::Function(Box::new(Function {
15140                            name: func_name,
15141                            args,
15142                            distinct: false,
15143                            trailing_comments: Vec::new(),
15144                            use_bracket_syntax: false,
15145                            no_parens: false,
15146                            quoted: false,
15147                            span: None,
15148                            inferred_type: None,
15149                        })));
15150                    } else {
15151                        // Try to parse as column definition (name data_type) for Hive-style partitioned by
15152                        // e.g., PARTITIONED BY (y INT, z STRING)
15153                        let saved_pos = self.current;
15154                        let mut parsed_as_column = false;
15155                        // Allow type keywords (like DATE, TIMESTAMP) as column names in PARTITIONED BY
15156                        if self.check(TokenType::Var)
15157                            || self.check(TokenType::Identifier)
15158                            || self.check(TokenType::Date)
15159                            || self.check(TokenType::Timestamp)
15160                            || self.check(TokenType::Int)
15161                            || self.check(TokenType::BigInt)
15162                            || self.check(TokenType::SmallInt)
15163                            || self.check(TokenType::TinyInt)
15164                            || self.check(TokenType::Float)
15165                            || self.check(TokenType::Double)
15166                            || self.check(TokenType::Boolean)
15167                        {
15168                            let col_name = self.advance().text.clone();
15169                            // Check if next token looks like a data type
15170                            if self.check(TokenType::Var)
15171                                || self.check(TokenType::Identifier)
15172                                || self.check(TokenType::Int)
15173                                || self.check(TokenType::BigInt)
15174                                || self.check(TokenType::SmallInt)
15175                                || self.check(TokenType::TinyInt)
15176                                || self.check(TokenType::Float)
15177                                || self.check(TokenType::Double)
15178                                || self.check(TokenType::Boolean)
15179                                || self.check(TokenType::Date)
15180                                || self.check(TokenType::Timestamp)
15181                            {
15182                                let type_text = self.peek().text.to_ascii_uppercase();
15183                                let is_type = matches!(
15184                                    type_text.as_str(),
15185                                    "INT"
15186                                        | "INTEGER"
15187                                        | "BIGINT"
15188                                        | "SMALLINT"
15189                                        | "TINYINT"
15190                                        | "FLOAT"
15191                                        | "DOUBLE"
15192                                        | "DECIMAL"
15193                                        | "NUMERIC"
15194                                        | "STRING"
15195                                        | "VARCHAR"
15196                                        | "CHAR"
15197                                        | "BINARY"
15198                                        | "BOOLEAN"
15199                                        | "DATE"
15200                                        | "TIMESTAMP"
15201                                        | "DATETIME"
15202                                        | "ARRAY"
15203                                        | "MAP"
15204                                        | "STRUCT"
15205                                );
15206                                if is_type {
15207                                    // Parse as column definition
15208                                    let data_type = self.parse_data_type()?;
15209                                    // Store as ColumnDef expression
15210                                    partition_exprs.push(Expression::ColumnDef(Box::new(
15211                                        crate::expressions::ColumnDef::new(col_name, data_type),
15212                                    )));
15213                                    parsed_as_column = true;
15214                                }
15215                            }
15216                        }
15217                        if !parsed_as_column {
15218                            // Backtrack and parse as regular expression
15219                            self.current = saved_pos;
15220                            partition_exprs.push(self.parse_expression()?);
15221                        }
15222                    }
15223
15224                    if !self.match_token(TokenType::Comma) {
15225                        break;
15226                    }
15227                }
15228                self.expect(TokenType::RParen)?;
15229
15230                properties.push(Expression::PartitionedByProperty(Box::new(
15231                    PartitionedByProperty {
15232                        this: Box::new(Expression::Tuple(Box::new(Tuple {
15233                            expressions: partition_exprs,
15234                        }))),
15235                    },
15236                )));
15237                continue;
15238            }
15239
15240            // No more Hive properties
15241            break;
15242        }
15243
15244        Ok(properties)
15245    }
15246
15247    /// Parse table-level properties that appear after the closing paren of column definitions.
15248    /// Currently handles TSQL WITH(SYSTEM_VERSIONING=ON(...)).
15249    fn parse_post_table_properties(&mut self) -> Result<Vec<Expression>> {
15250        let mut properties = Vec::new();
15251
15252        // Doris/StarRocks: UNIQUE KEY (cols) or DUPLICATE KEY (cols) after column definitions
15253        // These are table key properties that define the distribution/sort key
15254        let is_doris_starrocks = matches!(
15255            self.config.dialect,
15256            Some(crate::dialects::DialectType::Doris)
15257                | Some(crate::dialects::DialectType::StarRocks)
15258        );
15259        if is_doris_starrocks {
15260            // UNIQUE KEY (c1, c2, ...) - defines unique key columns
15261            if self.match_text_seq(&["UNIQUE", "KEY"]) {
15262                let exprs = self.parse_composite_key_expressions()?;
15263                properties.push(Expression::UniqueKeyProperty(Box::new(
15264                    crate::expressions::UniqueKeyProperty { expressions: exprs },
15265                )));
15266            }
15267            // DUPLICATE KEY (c1, c2, ...) - defines duplicate key columns
15268            else if self.match_text_seq(&["DUPLICATE", "KEY"]) {
15269                let exprs = self.parse_composite_key_expressions()?;
15270                properties.push(Expression::DuplicateKeyProperty(Box::new(
15271                    crate::expressions::DuplicateKeyProperty { expressions: exprs },
15272                )));
15273            }
15274
15275            // DISTRIBUTED BY HASH (col1, col2) [BUCKETS n] - comes after UNIQUE KEY / DUPLICATE KEY
15276            if self.match_identifier("DISTRIBUTED") {
15277                if let Some(dist_prop) = self.parse_distributed_property()? {
15278                    properties.push(dist_prop);
15279                }
15280            }
15281
15282            // PROPERTIES ('key'='value', ...) - comes after DISTRIBUTED BY
15283            if self.match_identifier("PROPERTIES") {
15284                let props = self.parse_options_list()?;
15285                if !props.is_empty() {
15286                    properties.push(Expression::Properties(Box::new(Properties {
15287                        expressions: props,
15288                    })));
15289                }
15290            }
15291        }
15292
15293        // Check for WITH( that might contain SYSTEM_VERSIONING
15294        // We need to be careful not to consume a WITH that is meant for WITH properties
15295        // or other purposes. We only handle WITH(SYSTEM_VERSIONING=...) here.
15296        if self.check(TokenType::With) {
15297            // Look ahead: WITH followed by ( followed by SYSTEM_VERSIONING
15298            let saved = self.current;
15299            if self.match_token(TokenType::With) {
15300                if self.match_token(TokenType::LParen) {
15301                    if self.check_identifier("SYSTEM_VERSIONING") {
15302                        self.skip(); // consume SYSTEM_VERSIONING
15303                        self.expect(TokenType::Eq)?;
15304
15305                        let on = if self.match_token(TokenType::On) {
15306                            true
15307                        } else if self.match_identifier("OFF") {
15308                            false
15309                        } else {
15310                            return Err(
15311                                self.parse_error("Expected ON or OFF after SYSTEM_VERSIONING=")
15312                            );
15313                        };
15314
15315                        let mut history_table = None;
15316                        let mut data_consistency = None;
15317
15318                        // Optional parameters: ON(HISTORY_TABLE=..., DATA_CONSISTENCY_CHECK=...)
15319                        if on && self.match_token(TokenType::LParen) {
15320                            loop {
15321                                if self.check(TokenType::RParen) {
15322                                    break;
15323                                }
15324                                if self.match_identifier("HISTORY_TABLE") {
15325                                    self.expect(TokenType::Eq)?;
15326                                    // Parse table reference (could be [dbo].[table])
15327                                    let table_ref = self.parse_table_ref()?;
15328                                    history_table = Some(Expression::Table(Box::new(table_ref)));
15329                                } else if self.match_identifier("DATA_CONSISTENCY_CHECK") {
15330                                    self.expect(TokenType::Eq)?;
15331                                    let val = self.expect_identifier_or_keyword()?;
15332                                    data_consistency = Some(Expression::Identifier(
15333                                        crate::expressions::Identifier::new(val),
15334                                    ));
15335                                } else if self.check(TokenType::RParen) {
15336                                    break;
15337                                } else {
15338                                    self.skip();
15339                                }
15340                                self.match_token(TokenType::Comma);
15341                            }
15342                            self.expect(TokenType::RParen)?;
15343                        }
15344
15345                        self.expect(TokenType::RParen)?; // close WITH(...)
15346
15347                        properties.push(Expression::WithSystemVersioningProperty(Box::new(
15348                            WithSystemVersioningProperty {
15349                                on: if on {
15350                                    Some(Box::new(Expression::Boolean(
15351                                        crate::expressions::BooleanLiteral { value: true },
15352                                    )))
15353                                } else {
15354                                    None
15355                                },
15356                                this: history_table.map(Box::new),
15357                                data_consistency: data_consistency.map(Box::new),
15358                                retention_period: None,
15359                                with_: Some(Box::new(Expression::Boolean(
15360                                    crate::expressions::BooleanLiteral { value: true },
15361                                ))),
15362                            },
15363                        )));
15364                    } else {
15365                        // Not SYSTEM_VERSIONING, retreat
15366                        self.current = saved;
15367                    }
15368                } else {
15369                    // Not WITH(...), retreat
15370                    self.current = saved;
15371                }
15372            }
15373        }
15374
15375        Ok(properties)
15376    }
15377
15378    /// Parse composite key expressions for UNIQUE KEY (cols) or DUPLICATE KEY (cols)
15379    /// Returns a vector of column identifiers
15380    fn parse_composite_key_expressions(&mut self) -> Result<Vec<Expression>> {
15381        self.expect(TokenType::LParen)?;
15382        let mut expressions = Vec::new();
15383        loop {
15384            if let Some(id) = self.parse_id_var()? {
15385                expressions.push(id);
15386            } else {
15387                break;
15388            }
15389            if !self.match_token(TokenType::Comma) {
15390                break;
15391            }
15392        }
15393        self.expect(TokenType::RParen)?;
15394        Ok(expressions)
15395    }
15396
15397    /// Parse a table-level constraint
15398    fn parse_table_constraint(&mut self) -> Result<TableConstraint> {
15399        // Optional constraint name
15400        let name = if self.match_token(TokenType::Constraint) {
15401            // Use safe keyword version to accept keywords as constraint names (e.g., CONSTRAINT identity CHECK ...)
15402            Some(self.expect_identifier_or_safe_keyword_with_quoted()?)
15403        } else {
15404            None
15405        };
15406
15407        self.parse_constraint_definition(name)
15408    }
15409
15410    /// Parse constraint definition (after optional CONSTRAINT name)
15411    fn parse_constraint_definition(&mut self, name: Option<Identifier>) -> Result<TableConstraint> {
15412        if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
15413            // PRIMARY KEY [CLUSTERED|NONCLUSTERED] [name] (col1, col2) [INCLUDE (col3, col4)]
15414            // MySQL allows: PRIMARY KEY pk_name (col1, col2)
15415            // TSQL allows: PRIMARY KEY CLUSTERED (col1, col2)
15416
15417            // Check for TSQL CLUSTERED/NONCLUSTERED modifier
15418            let clustered = if self.check_identifier("CLUSTERED") {
15419                self.skip();
15420                Some("CLUSTERED".to_string())
15421            } else if self.check_identifier("NONCLUSTERED") {
15422                self.skip();
15423                Some("NONCLUSTERED".to_string())
15424            } else {
15425                None
15426            };
15427
15428            let actual_name = if name.is_none() && !self.check(TokenType::LParen) {
15429                if matches!(
15430                    self.config.dialect,
15431                    Some(crate::dialects::DialectType::ClickHouse)
15432                ) {
15433                    // ClickHouse: PRIMARY KEY col (without parentheses)
15434                    None
15435                } else if self.is_identifier_token() || self.check(TokenType::QuotedIdentifier) {
15436                    Some(self.expect_identifier_with_quoted()?)
15437                } else if self.check(TokenType::String)
15438                    && matches!(
15439                        self.config.dialect,
15440                        Some(crate::dialects::DialectType::MySQL)
15441                    )
15442                {
15443                    // MySQL: double-quoted strings can be used as constraint names
15444                    // e.g., PRIMARY KEY "pk_name" (id) -> PRIMARY KEY `pk_name` (id)
15445                    let s = self.advance().text.clone();
15446                    Some(Identifier {
15447                        name: s,
15448                        quoted: true,
15449                        trailing_comments: Vec::new(),
15450                        span: None,
15451                    })
15452                } else {
15453                    None
15454                }
15455            } else {
15456                name.clone()
15457            };
15458            // ClickHouse: PRIMARY KEY col without parens — parse single column
15459            let columns = if matches!(
15460                self.config.dialect,
15461                Some(crate::dialects::DialectType::ClickHouse)
15462            ) && !self.check(TokenType::LParen)
15463                && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
15464            {
15465                let col_name = self.expect_identifier_or_keyword_with_quoted()?;
15466                vec![col_name]
15467            } else {
15468                self.expect(TokenType::LParen)?;
15469                // ClickHouse: allow empty PRIMARY KEY ()
15470                let cols = if self.check(TokenType::RParen) {
15471                    Vec::new()
15472                } else if matches!(
15473                    self.config.dialect,
15474                    Some(crate::dialects::DialectType::ClickHouse)
15475                ) {
15476                    // ClickHouse: PRIMARY KEY(v1, gcd(v1, v2)) - expressions allowed
15477                    let mut exprs = Vec::new();
15478                    loop {
15479                        let expr = self.parse_expression()?;
15480                        let name = self.expression_to_sql(&expr);
15481                        exprs.push(Identifier::new(name));
15482                        if !self.match_token(TokenType::Comma) {
15483                            break;
15484                        }
15485                    }
15486                    exprs
15487                } else {
15488                    self.parse_index_identifier_list()?
15489                };
15490                self.expect(TokenType::RParen)?;
15491                cols
15492            };
15493            // Parse optional INCLUDE (columns)
15494            let include_columns = if self.match_identifier("INCLUDE") {
15495                self.expect(TokenType::LParen)?;
15496                let cols = self.parse_identifier_list()?;
15497                self.expect(TokenType::RParen)?;
15498                cols
15499            } else {
15500                Vec::new()
15501            };
15502            // Parse optional constraint modifiers (ENFORCED, DEFERRABLE, etc.)
15503            let mut modifiers = self.parse_constraint_modifiers();
15504            modifiers.clustered = clustered;
15505            let has_constraint_keyword = name.is_some();
15506            Ok(TableConstraint::PrimaryKey {
15507                name: actual_name.or(name),
15508                columns,
15509                include_columns,
15510                modifiers,
15511                has_constraint_keyword,
15512            })
15513        } else if self.match_token(TokenType::Unique) {
15514            // UNIQUE [CLUSTERED|NONCLUSTERED] [KEY|INDEX] [NULLS NOT DISTINCT] [name] (col1, col2) or UNIQUE column_name
15515            // MySQL allows: UNIQUE KEY name (cols), UNIQUE INDEX name (cols), UNIQUE (cols)
15516            // TSQL allows: UNIQUE CLUSTERED (cols)
15517            // PostgreSQL 15+: UNIQUE NULLS NOT DISTINCT (cols)
15518
15519            // Check for TSQL CLUSTERED/NONCLUSTERED modifier
15520            let clustered = if self.check_identifier("CLUSTERED") {
15521                self.skip();
15522                Some("CLUSTERED".to_string())
15523            } else if self.check_identifier("NONCLUSTERED") {
15524                self.skip();
15525                Some("NONCLUSTERED".to_string())
15526            } else {
15527                None
15528            };
15529
15530            let use_key_keyword =
15531                self.match_token(TokenType::Key) || self.match_token(TokenType::Index);
15532
15533            // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
15534            let nulls_not_distinct = self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]);
15535
15536            // Check for optional constraint name (before columns)
15537            let actual_name = if name.is_none()
15538                && self.is_identifier_token()
15539                && !self.check_next(TokenType::Comma)
15540            {
15541                // Name might be here: UNIQUE KEY idx_name (cols)
15542                if self.check_next(TokenType::LParen) {
15543                    Some(self.expect_identifier_with_quoted()?)
15544                } else {
15545                    None
15546                }
15547            } else {
15548                name.clone()
15549            };
15550
15551            if self.match_token(TokenType::LParen) {
15552                let columns = self.parse_index_identifier_list()?;
15553                self.expect(TokenType::RParen)?;
15554                let mut modifiers = self.parse_constraint_modifiers();
15555                modifiers.clustered = clustered;
15556                if use_key_keyword {
15557                    // UNIQUE KEY/INDEX - use Index constraint type with UNIQUE kind
15558                    Ok(TableConstraint::Index {
15559                        name: actual_name.or(name),
15560                        columns,
15561                        kind: Some("UNIQUE".to_string()),
15562                        modifiers,
15563                        use_key_keyword,
15564                        expression: None,
15565                        index_type: None,
15566                        granularity: None,
15567                    })
15568                } else {
15569                    let has_constraint_keyword = name.is_some();
15570                    Ok(TableConstraint::Unique {
15571                        name: actual_name.or(name),
15572                        columns,
15573                        columns_parenthesized: true,
15574                        modifiers,
15575                        has_constraint_keyword,
15576                        nulls_not_distinct,
15577                    })
15578                }
15579            } else {
15580                // Single column unique (for ALTER TABLE ADD CONSTRAINT name UNIQUE colname)
15581                let col_name = self.expect_identifier()?;
15582                let mut modifiers = self.parse_constraint_modifiers();
15583                modifiers.clustered = clustered;
15584                let has_constraint_keyword = name.is_some();
15585                Ok(TableConstraint::Unique {
15586                    name: actual_name.or(name),
15587                    columns: vec![Identifier::new(col_name)],
15588                    columns_parenthesized: false,
15589                    modifiers,
15590                    has_constraint_keyword,
15591                    nulls_not_distinct,
15592                })
15593            }
15594        } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
15595            // FOREIGN KEY (col1) [REFERENCES other_table(col2)] [ON DELETE ...] [ON UPDATE ...]
15596            self.expect(TokenType::LParen)?;
15597            let columns = self.parse_identifier_list()?;
15598            self.expect(TokenType::RParen)?;
15599            if self.match_token(TokenType::References) {
15600                let references = self.parse_foreign_key_ref()?;
15601                let modifiers = self.parse_constraint_modifiers();
15602                Ok(TableConstraint::ForeignKey {
15603                    name,
15604                    columns,
15605                    references: Some(references),
15606                    on_delete: None,
15607                    on_update: None,
15608                    modifiers,
15609                })
15610            } else {
15611                // No REFERENCES - parse optional ON DELETE/ON UPDATE directly
15612                let mut on_delete = None;
15613                let mut on_update = None;
15614                loop {
15615                    if self.check(TokenType::On) {
15616                        let saved = self.current;
15617                        self.skip(); // consume ON
15618                        if self.match_token(TokenType::Delete) {
15619                            on_delete = Some(self.parse_referential_action()?);
15620                        } else if self.match_token(TokenType::Update) {
15621                            on_update = Some(self.parse_referential_action()?);
15622                        } else {
15623                            self.current = saved;
15624                            break;
15625                        }
15626                    } else {
15627                        break;
15628                    }
15629                }
15630                let modifiers = self.parse_constraint_modifiers();
15631                Ok(TableConstraint::ForeignKey {
15632                    name,
15633                    columns,
15634                    references: None,
15635                    on_delete,
15636                    on_update,
15637                    modifiers,
15638                })
15639            }
15640        } else if self.match_token(TokenType::Check) {
15641            // CHECK (expression) or CHECK (SELECT ...) or ClickHouse: CHECK expression (without parens)
15642            let expression = if self.match_token(TokenType::LParen) {
15643                let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
15644                    // SELECT/WITH in CHECK constraint — parse directly, no Subquery wrapper
15645                    // The generator already wraps CHECK content in parens
15646                    self.parse_statement()?
15647                } else {
15648                    self.parse_expression()?
15649                };
15650                self.expect(TokenType::RParen)?;
15651                expr
15652            } else if matches!(
15653                self.config.dialect,
15654                Some(crate::dialects::DialectType::ClickHouse)
15655            ) {
15656                self.parse_or()?
15657            } else {
15658                self.expect(TokenType::LParen)?;
15659                unreachable!()
15660            };
15661            let modifiers = self.parse_constraint_modifiers();
15662            Ok(TableConstraint::Check {
15663                name,
15664                expression,
15665                modifiers,
15666            })
15667        } else if self.match_token(TokenType::Exclude) {
15668            // PostgreSQL EXCLUDE constraint
15669            // EXCLUDE [USING method] (element WITH operator, ...) [INCLUDE (cols)] [WHERE (expr)] [WITH (params)]
15670            let using = if self.match_token(TokenType::Using) {
15671                Some(self.expect_identifier()?)
15672            } else {
15673                None
15674            };
15675
15676            self.expect(TokenType::LParen)?;
15677            let mut elements = Vec::new();
15678            loop {
15679                // Parse element expression: may be a function call like INT4RANGE(vid, nid)
15680                // or column name possibly with operator class, ASC/DESC, NULLS FIRST/LAST
15681                let mut expr_parts = Vec::new();
15682                let mut paren_depth = 0;
15683                while !self.is_at_end() {
15684                    if self.check(TokenType::LParen) {
15685                        paren_depth += 1;
15686                        expr_parts.push(self.advance().text);
15687                    } else if self.check(TokenType::RParen) {
15688                        if paren_depth == 0 {
15689                            break;
15690                        }
15691                        paren_depth -= 1;
15692                        expr_parts.push(self.advance().text);
15693                    } else if paren_depth == 0 && self.check(TokenType::With) {
15694                        break;
15695                    } else if self.check(TokenType::String) {
15696                        // Preserve string literal quotes
15697                        let token = self.advance();
15698                        expr_parts.push(format!("'{}'", token.text));
15699                    } else {
15700                        expr_parts.push(self.advance().text);
15701                    }
15702                }
15703                let expression = expr_parts
15704                    .join(" ")
15705                    .replace(" (", "(")
15706                    .replace(" )", ")")
15707                    .replace("( ", "(")
15708                    .replace(" ,", ",");
15709
15710                // Parse WITH operator
15711                self.expect(TokenType::With)?;
15712                let operator = self.advance().text.clone();
15713
15714                elements.push(ExcludeElement {
15715                    expression,
15716                    operator,
15717                });
15718
15719                if !self.match_token(TokenType::Comma) {
15720                    break;
15721                }
15722            }
15723            self.expect(TokenType::RParen)?;
15724
15725            // Parse optional INCLUDE (columns)
15726            let include_columns = if self.match_identifier("INCLUDE") {
15727                self.expect(TokenType::LParen)?;
15728                let cols = self.parse_identifier_list()?;
15729                self.expect(TokenType::RParen)?;
15730                cols
15731            } else {
15732                Vec::new()
15733            };
15734
15735            // Parse optional WITH (storage_parameters)
15736            let with_params = if self.match_token(TokenType::With) {
15737                self.expect(TokenType::LParen)?;
15738                let mut params = Vec::new();
15739                loop {
15740                    let key = self.expect_identifier()?;
15741                    self.expect(TokenType::Eq)?;
15742                    let val = self.advance().text.clone();
15743                    params.push((key, val));
15744                    if !self.match_token(TokenType::Comma) {
15745                        break;
15746                    }
15747                }
15748                self.expect(TokenType::RParen)?;
15749                params
15750            } else {
15751                Vec::new()
15752            };
15753
15754            // Parse optional USING INDEX TABLESPACE tablespace_name
15755            let using_index_tablespace =
15756                if self.check(TokenType::Using) && self.check_next(TokenType::Index) {
15757                    self.skip(); // consume USING
15758                    self.skip(); // consume INDEX
15759                    if self.match_identifier("TABLESPACE") {
15760                        Some(self.expect_identifier()?)
15761                    } else {
15762                        None
15763                    }
15764                } else {
15765                    None
15766                };
15767
15768            // Parse optional WHERE clause
15769            let where_clause = if self.match_token(TokenType::Where) {
15770                self.expect(TokenType::LParen)?;
15771                let expr = self.parse_expression()?;
15772                self.expect(TokenType::RParen)?;
15773                Some(Box::new(expr))
15774            } else {
15775                None
15776            };
15777
15778            let modifiers = self.parse_constraint_modifiers();
15779            Ok(TableConstraint::Exclude {
15780                name,
15781                using,
15782                elements,
15783                include_columns,
15784                where_clause,
15785                with_params,
15786                using_index_tablespace,
15787                modifiers,
15788            })
15789        } else if matches!(
15790            self.config.dialect,
15791            Some(crate::dialects::DialectType::ClickHouse)
15792        ) && self.check_identifier("ASSUME")
15793        {
15794            // ClickHouse: CONSTRAINT name ASSUME expression
15795            // Used for query optimization assumptions
15796            self.skip(); // consume ASSUME
15797            let expression = if self.match_token(TokenType::LParen) {
15798                // ASSUME (expr) or ASSUME (SELECT ...)
15799                let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
15800                    self.parse_statement()?
15801                } else {
15802                    self.parse_expression()?
15803                };
15804                self.expect(TokenType::RParen)?;
15805                expr
15806            } else {
15807                self.parse_expression()?
15808            };
15809            Ok(TableConstraint::Assume { name, expression })
15810        } else {
15811            Err(self.parse_error("Expected PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK, or EXCLUDE"))
15812        }
15813    }
15814
15815    /// Parse INDEX/KEY table constraint for MySQL
15816    /// Syntax: [FULLTEXT|SPATIAL] {INDEX|KEY} [name] [USING {BTREE|HASH}] (columns)
15817    ///     or: [FULLTEXT|SPATIAL] {INDEX|KEY} [USING {BTREE|HASH}] (columns)  -- no name
15818    fn parse_index_table_constraint(&mut self) -> Result<TableConstraint> {
15819        // Check for FULLTEXT or SPATIAL prefix
15820        let kind = if self.match_identifier("FULLTEXT") {
15821            Some("FULLTEXT".to_string())
15822        } else if self.match_identifier("SPATIAL") {
15823            Some("SPATIAL".to_string())
15824        } else {
15825            None
15826        };
15827
15828        // Consume INDEX or KEY keyword, track which was used
15829        let use_key_keyword = if self.match_token(TokenType::Key) {
15830            true
15831        } else {
15832            self.match_token(TokenType::Index);
15833            false
15834        };
15835
15836        // Check for USING before index name (MySQL allows: INDEX USING BTREE (col))
15837        let early_using = if self.check(TokenType::Using) {
15838            self.match_token(TokenType::Using);
15839            if self.match_identifier("BTREE") {
15840                Some("BTREE".to_string())
15841            } else if self.match_identifier("HASH") {
15842                Some("HASH".to_string())
15843            } else {
15844                None
15845            }
15846        } else {
15847            None
15848        };
15849
15850        // Optional index name (only if next token is not LParen or Using)
15851        let name = if !self.check(TokenType::LParen)
15852            && !self.check(TokenType::Using)
15853            && self.is_identifier_token()
15854        {
15855            Some(Identifier::new(self.advance().text))
15856        } else {
15857            None
15858        };
15859
15860        // Check for USING after index name (if not already parsed)
15861        let late_using = if early_using.is_none() && self.match_token(TokenType::Using) {
15862            if self.match_identifier("BTREE") {
15863                Some("BTREE".to_string())
15864            } else if self.match_identifier("HASH") {
15865                Some("HASH".to_string())
15866            } else {
15867                None
15868            }
15869        } else {
15870            None
15871        };
15872
15873        // Parse columns (with optional prefix length and DESC)
15874        self.expect(TokenType::LParen)?;
15875        let columns = self.parse_index_identifier_list()?;
15876        self.expect(TokenType::RParen)?;
15877
15878        // Parse optional constraint modifiers (USING after columns, COMMENT, etc.)
15879        let mut modifiers = self.parse_constraint_modifiers();
15880
15881        // Set the using value from wherever we found it
15882        // Both early_using (before name) and late_using (after name, before columns) mean USING is before columns
15883        if early_using.is_some() {
15884            modifiers.using = early_using;
15885            modifiers.using_before_columns = true;
15886        } else if late_using.is_some() {
15887            modifiers.using = late_using;
15888            modifiers.using_before_columns = true; // USING was after name but before columns
15889        }
15890        // If using was found in parse_constraint_modifiers (after columns), using_before_columns stays false
15891
15892        Ok(TableConstraint::Index {
15893            name,
15894            columns,
15895            kind,
15896            modifiers,
15897            use_key_keyword,
15898            expression: None,
15899            index_type: None,
15900            granularity: None,
15901        })
15902    }
15903
15904    /// Parse constraint modifiers like ENFORCED, DEFERRABLE, NORELY, USING, etc.
15905    fn parse_constraint_modifiers(&mut self) -> ConstraintModifiers {
15906        let mut modifiers = ConstraintModifiers::default();
15907        loop {
15908            if self.match_token(TokenType::Not) {
15909                // NOT ENFORCED, NOT DEFERRABLE, NOT VALID
15910                if self.match_identifier("ENFORCED") {
15911                    modifiers.enforced = Some(false);
15912                } else if self.match_identifier("DEFERRABLE") {
15913                    modifiers.deferrable = Some(false);
15914                } else if self.match_identifier("VALID") {
15915                    modifiers.not_valid = true;
15916                }
15917            } else if self.match_identifier("ENFORCED") {
15918                modifiers.enforced = Some(true);
15919            } else if self.match_identifier("DEFERRABLE") {
15920                modifiers.deferrable = Some(true);
15921            } else if self.match_identifier("INITIALLY") {
15922                // INITIALLY DEFERRED or INITIALLY IMMEDIATE
15923                if self.match_identifier("DEFERRED") {
15924                    modifiers.initially_deferred = Some(true);
15925                } else if self.match_identifier("IMMEDIATE") {
15926                    modifiers.initially_deferred = Some(false);
15927                }
15928            } else if self.match_identifier("NORELY") {
15929                modifiers.norely = true;
15930            } else if self.match_identifier("RELY") {
15931                modifiers.rely = true;
15932            } else if self.match_token(TokenType::Using) {
15933                // USING BTREE or USING HASH (MySQL)
15934                if self.match_identifier("BTREE") {
15935                    modifiers.using = Some("BTREE".to_string());
15936                } else if self.match_identifier("HASH") {
15937                    modifiers.using = Some("HASH".to_string());
15938                }
15939            } else if self.match_token(TokenType::Comment) {
15940                // MySQL index COMMENT 'text'
15941                if self.check(TokenType::String) {
15942                    modifiers.comment = Some(self.advance().text);
15943                }
15944            } else if self.match_identifier("VISIBLE") {
15945                modifiers.visible = Some(true);
15946            } else if self.match_identifier("INVISIBLE") {
15947                modifiers.visible = Some(false);
15948            } else if self.match_identifier("ENGINE_ATTRIBUTE") {
15949                // MySQL ENGINE_ATTRIBUTE = 'value'
15950                self.match_token(TokenType::Eq);
15951                if self.check(TokenType::String) {
15952                    modifiers.engine_attribute = Some(self.advance().text);
15953                }
15954            } else if self.check(TokenType::With) {
15955                let saved_with = self.current;
15956                self.skip(); // consume WITH
15957                if self.match_identifier("PARSER") {
15958                    // MySQL WITH PARSER name
15959                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
15960                        modifiers.with_parser = Some(self.advance().text);
15961                    }
15962                } else if self.check(TokenType::LParen) {
15963                    // TSQL: WITH (PAD_INDEX=ON, STATISTICS_NORECOMPUTE=OFF, ...)
15964                    // Parse and store the options
15965                    self.skip(); // consume (
15966                    loop {
15967                        if self.check(TokenType::RParen) || self.is_at_end() {
15968                            break;
15969                        }
15970                        // Parse KEY=VALUE pair
15971                        let key = self.advance().text.clone();
15972                        if self.match_token(TokenType::Eq) {
15973                            let value = self.advance().text.clone();
15974                            modifiers.with_options.push((key, value));
15975                        }
15976                        if !self.match_token(TokenType::Comma) {
15977                            break;
15978                        }
15979                    }
15980                    let _ = self.match_token(TokenType::RParen);
15981                } else {
15982                    // Not WITH PARSER or WITH (...), backtrack
15983                    self.current = saved_with;
15984                    break;
15985                }
15986            } else if self.check(TokenType::On) {
15987                let saved_on = self.current;
15988                self.skip(); // consume ON
15989                if self.match_identifier("CONFLICT") {
15990                    // SQLite ON CONFLICT action: ROLLBACK, ABORT, FAIL, IGNORE, REPLACE
15991                    if self.match_token(TokenType::Rollback) {
15992                        modifiers.on_conflict = Some("ROLLBACK".to_string());
15993                    } else if self.match_identifier("ABORT") {
15994                        modifiers.on_conflict = Some("ABORT".to_string());
15995                    } else if self.match_identifier("FAIL") {
15996                        modifiers.on_conflict = Some("FAIL".to_string());
15997                    } else if self.match_token(TokenType::Ignore) {
15998                        modifiers.on_conflict = Some("IGNORE".to_string());
15999                    } else if self.match_token(TokenType::Replace) {
16000                        modifiers.on_conflict = Some("REPLACE".to_string());
16001                    }
16002                } else if self.is_identifier_token() || self.check(TokenType::QuotedIdentifier) {
16003                    // TSQL: ON [filegroup] - parse and store
16004                    let quoted = self.check(TokenType::QuotedIdentifier);
16005                    let name = self.advance().text.clone();
16006                    modifiers.on_filegroup = Some(Identifier {
16007                        name,
16008                        quoted,
16009                        trailing_comments: Vec::new(),
16010                        span: None,
16011                    });
16012                } else {
16013                    // Unknown ON clause, backtrack
16014                    self.current = saved_on;
16015                    break;
16016                }
16017            } else {
16018                break;
16019            }
16020        }
16021        modifiers
16022    }
16023
16024    /// Parse foreign key reference
16025    fn parse_foreign_key_ref(&mut self) -> Result<ForeignKeyRef> {
16026        let table = self.parse_table_ref()?;
16027
16028        let columns = if self.match_token(TokenType::LParen) {
16029            let cols = self.parse_identifier_list()?;
16030            self.expect(TokenType::RParen)?;
16031            cols
16032        } else {
16033            Vec::new()
16034        };
16035
16036        // Handle optional MATCH clause (MATCH FULL, MATCH PARTIAL, MATCH SIMPLE)
16037        // MATCH clause comes BEFORE ON DELETE/ON UPDATE in PostgreSQL
16038        let match_type = if self.match_token(TokenType::Match) {
16039            if self.check(TokenType::Full) {
16040                self.skip();
16041                Some(MatchType::Full)
16042            } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
16043                let text = self.advance().text.to_ascii_uppercase();
16044                match text.as_str() {
16045                    "PARTIAL" => Some(MatchType::Partial),
16046                    "SIMPLE" => Some(MatchType::Simple),
16047                    _ => None,
16048                }
16049            } else {
16050                None
16051            }
16052        } else {
16053            None
16054        };
16055
16056        // ON DELETE and ON UPDATE can appear in either order
16057        let mut on_delete = None;
16058        let mut on_update = None;
16059        let mut on_update_first = false;
16060        let mut first_clause = true;
16061
16062        // Try parsing up to 2 ON clauses
16063        for _ in 0..2 {
16064            if on_delete.is_none() && self.match_keywords(&[TokenType::On, TokenType::Delete]) {
16065                on_delete = Some(self.parse_referential_action()?);
16066            } else if on_update.is_none()
16067                && self.match_keywords(&[TokenType::On, TokenType::Update])
16068            {
16069                if first_clause {
16070                    on_update_first = true;
16071                }
16072                on_update = Some(self.parse_referential_action()?);
16073            } else {
16074                break;
16075            }
16076            first_clause = false;
16077        }
16078
16079        // MATCH clause can also appear after ON DELETE/ON UPDATE
16080        let mut match_after_actions = false;
16081        let match_type = if match_type.is_none() && self.match_token(TokenType::Match) {
16082            match_after_actions = on_delete.is_some() || on_update.is_some();
16083            if self.check(TokenType::Full) {
16084                self.skip();
16085                Some(MatchType::Full)
16086            } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
16087                let text = self.advance().text.to_ascii_uppercase();
16088                match text.as_str() {
16089                    "PARTIAL" => Some(MatchType::Partial),
16090                    "SIMPLE" => Some(MatchType::Simple),
16091                    _ => None,
16092                }
16093            } else {
16094                None
16095            }
16096        } else {
16097            match_type
16098        };
16099
16100        // Handle optional DEFERRABLE / NOT DEFERRABLE
16101        let deferrable = if self.match_identifier("DEFERRABLE") {
16102            Some(true)
16103        } else if self.match_token(TokenType::Not) && self.match_identifier("DEFERRABLE") {
16104            Some(false)
16105        } else {
16106            None
16107        };
16108
16109        Ok(ForeignKeyRef {
16110            table,
16111            columns,
16112            on_delete,
16113            on_update,
16114            on_update_first,
16115            match_type,
16116            match_after_actions,
16117            constraint_name: None, // Will be set by caller if CONSTRAINT was used
16118            deferrable,
16119            has_foreign_key_keywords: false, // Will be set by caller if FOREIGN KEY preceded REFERENCES
16120        })
16121    }
16122
16123    /// Parse referential action (CASCADE, SET NULL, etc.)
16124    fn parse_referential_action(&mut self) -> Result<ReferentialAction> {
16125        if self.match_token(TokenType::Cascade) {
16126            Ok(ReferentialAction::Cascade)
16127        } else if self.match_keywords(&[TokenType::Set, TokenType::Null]) {
16128            Ok(ReferentialAction::SetNull)
16129        } else if self.match_keywords(&[TokenType::Set, TokenType::Default]) {
16130            Ok(ReferentialAction::SetDefault)
16131        } else if self.match_token(TokenType::Restrict) {
16132            Ok(ReferentialAction::Restrict)
16133        } else if self.match_token(TokenType::No) {
16134            // NO ACTION - NO is a token, ACTION is an identifier
16135            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ACTION") {
16136                self.skip();
16137            }
16138            Ok(ReferentialAction::NoAction)
16139        } else {
16140            Err(self.parse_error("Expected CASCADE, SET NULL, SET DEFAULT, RESTRICT, or NO ACTION"))
16141        }
16142    }
16143
16144    /// Parse Snowflake TAG clause: TAG (key='value', key2='value2')
16145    fn parse_tags(&mut self) -> Result<Tags> {
16146        self.expect(TokenType::LParen)?;
16147        let mut expressions = Vec::new();
16148
16149        loop {
16150            // Parse key = 'value' as a Property expression
16151            let key = self.expect_identifier_or_keyword()?;
16152            self.expect(TokenType::Eq)?;
16153            let value = self.parse_primary()?;
16154
16155            // Create a Property expression: key = value
16156            expressions.push(Expression::Property(Box::new(Property {
16157                this: Box::new(Expression::Identifier(Identifier::new(key))),
16158                value: Some(Box::new(value)),
16159            })));
16160
16161            if !self.match_token(TokenType::Comma) {
16162                break;
16163            }
16164        }
16165
16166        self.expect(TokenType::RParen)?;
16167
16168        Ok(Tags { expressions })
16169    }
16170
16171    /// Parse CREATE VIEW
16172    fn parse_create_view(
16173        &mut self,
16174        or_replace: bool,
16175        or_alter: bool,
16176        materialized: bool,
16177        temporary: bool,
16178        algorithm: Option<String>,
16179        definer: Option<String>,
16180        security: Option<FunctionSecurity>,
16181        secure: bool,
16182    ) -> Result<Expression> {
16183        self.expect(TokenType::View)?;
16184
16185        // Handle IF NOT EXISTS
16186        let if_not_exists =
16187            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
16188
16189        let name = self.parse_table_ref()?;
16190
16191        // ClickHouse: UUID 'xxx' clause after view name
16192        if matches!(
16193            self.config.dialect,
16194            Some(crate::dialects::DialectType::ClickHouse)
16195        ) && self.check_identifier("UUID")
16196        {
16197            self.skip(); // consume UUID
16198            let _ = self.advance(); // consume UUID string value
16199        }
16200
16201        // ClickHouse: ON CLUSTER clause (after view name)
16202        let on_cluster = self.parse_on_cluster_clause()?;
16203
16204        // ClickHouse: TO destination_table clause
16205        let to_table = if self.match_token(TokenType::To) {
16206            Some(self.parse_table_ref()?)
16207        } else {
16208            None
16209        };
16210
16211        // Snowflake: COPY GRANTS (before column list)
16212        let copy_grants = self.match_text_seq(&["COPY", "GRANTS"]);
16213
16214        // For materialized views, column definitions can include data types: (c1 INT, c2 INT)
16215        // This applies to Doris, ClickHouse, and potentially other dialects
16216        // We need to parse this as a schema instead of simple column names
16217        // Track if we parsed a schema (with types) vs simple columns
16218        let mut schema: Option<Schema> = None;
16219        let mut unique_key: Option<UniqueKeyProperty> = None;
16220
16221        // Optional column list with optional COMMENT and OPTIONS per column
16222        let columns = if self.check(TokenType::LParen) {
16223            // For materialized views or ClickHouse views, try to parse as schema with typed columns
16224            if materialized
16225                || matches!(
16226                    self.config.dialect,
16227                    Some(crate::dialects::DialectType::ClickHouse)
16228                )
16229            {
16230                // Save position to backtrack if needed
16231                let saved_pos = self.current;
16232
16233                // Try to parse as schema (with typed columns)
16234                if let Some(Expression::Schema(parsed_schema)) = self.parse_schema()? {
16235                    schema = Some(*parsed_schema);
16236
16237                    // Doris: KEY (columns) after schema
16238                    if self.match_text_seq(&["KEY"]) {
16239                        let exprs = self.parse_composite_key_expressions()?;
16240                        unique_key = Some(UniqueKeyProperty { expressions: exprs });
16241                    }
16242
16243                    Vec::new() // Use schema instead of columns
16244                } else {
16245                    // Backtrack and parse as simple columns
16246                    self.current = saved_pos;
16247                    self.parse_view_columns()?
16248                }
16249            } else {
16250                self.parse_view_columns()?
16251            }
16252        } else {
16253            Vec::new()
16254        };
16255
16256        // Snowflake: COPY GRANTS can also appear after column list
16257        let copy_grants = copy_grants || self.match_text_seq(&["COPY", "GRANTS"]);
16258
16259        // Presto/Trino/StarRocks: SECURITY DEFINER/INVOKER/NONE (after view name, before AS)
16260        // MySQL also allows SQL SECURITY DEFINER/INVOKER after the view name
16261        // This differs from MySQL's SQL SECURITY which can also come before VIEW keyword
16262        let (security, security_sql_style, security_after_name) = if security.is_some() {
16263            // MySQL-style SQL SECURITY was parsed before VIEW keyword
16264            (security, true, false)
16265        } else if self.check_identifier("SQL")
16266            && self.current + 1 < self.tokens.len()
16267            && self.tokens[self.current + 1]
16268                .text
16269                .eq_ignore_ascii_case("SECURITY")
16270        {
16271            // SQL SECURITY after view name
16272            self.skip(); // consume SQL
16273            self.skip(); // consume SECURITY
16274            let sec = if self.match_identifier("DEFINER") {
16275                Some(FunctionSecurity::Definer)
16276            } else if self.match_identifier("INVOKER") {
16277                Some(FunctionSecurity::Invoker)
16278            } else if self.match_identifier("NONE") {
16279                Some(FunctionSecurity::None)
16280            } else {
16281                None
16282            };
16283            (sec, true, true)
16284        } else if self.match_identifier("SECURITY") {
16285            // Presto-style SECURITY after view name
16286            let sec = if self.match_identifier("DEFINER") {
16287                Some(FunctionSecurity::Definer)
16288            } else if self.match_identifier("INVOKER") {
16289                Some(FunctionSecurity::Invoker)
16290            } else if self.match_identifier("NONE") {
16291                Some(FunctionSecurity::None)
16292            } else {
16293                None
16294            };
16295            (sec, false, false)
16296        } else {
16297            (None, true, false)
16298        };
16299
16300        // Snowflake: COMMENT = 'text'
16301        let view_comment = if self.match_token(TokenType::Comment) {
16302            // Match = or skip if not present (some dialects use COMMENT='text')
16303            let _ = self.match_token(TokenType::Eq);
16304            Some(self.expect_string()?)
16305        } else {
16306            None
16307        };
16308
16309        // Snowflake: TAG (name='value', ...)
16310        let tags = if self.match_identifier("TAG") {
16311            let mut tag_list = Vec::new();
16312            if self.match_token(TokenType::LParen) {
16313                loop {
16314                    let tag_name = self.expect_identifier()?;
16315                    let tag_value = if self.match_token(TokenType::Eq) {
16316                        self.expect_string()?
16317                    } else {
16318                        String::new()
16319                    };
16320                    tag_list.push((tag_name, tag_value));
16321                    if !self.match_token(TokenType::Comma) {
16322                        break;
16323                    }
16324                }
16325                self.expect(TokenType::RParen)?;
16326            }
16327            tag_list
16328        } else {
16329            Vec::new()
16330        };
16331
16332        // BigQuery: OPTIONS (key=value, ...)
16333        let options = if self.match_identifier("OPTIONS") {
16334            self.parse_options_list()?
16335        } else {
16336            Vec::new()
16337        };
16338
16339        // Doris: BUILD IMMEDIATE/DEFERRED for materialized views
16340        let build = if self.match_identifier("BUILD") {
16341            if self.match_identifier("IMMEDIATE") {
16342                Some("IMMEDIATE".to_string())
16343            } else if self.match_identifier("DEFERRED") {
16344                Some("DEFERRED".to_string())
16345            } else {
16346                // Unexpected token after BUILD - try to consume it
16347                let value = self.expect_identifier_or_keyword()?;
16348                Some(value.to_ascii_uppercase())
16349            }
16350        } else {
16351            None
16352        };
16353
16354        // Doris: REFRESH COMPLETE/AUTO ON MANUAL/COMMIT/SCHEDULE [EVERY n UNIT] [STARTS 'datetime']
16355        // ClickHouse: REFRESH AFTER interval / REFRESH EVERY interval [OFFSET interval] [RANDOMIZE FOR interval] [APPEND]
16356        let refresh = if self.match_token(TokenType::Refresh) {
16357            if matches!(
16358                self.config.dialect,
16359                Some(crate::dialects::DialectType::ClickHouse)
16360            ) {
16361                // ClickHouse REFRESH syntax: consume tokens until AS/POPULATE/TO/ENGINE or end
16362                while !self.is_at_end()
16363                    && !self.check(TokenType::As)
16364                    && !self.check_identifier("POPULATE")
16365                    && !self.check_identifier("TO")
16366                    && !self.check_identifier("APPEND")
16367                    && !self.check_identifier("ENGINE")
16368                    && !self.check(TokenType::Semicolon)
16369                {
16370                    self.skip();
16371                }
16372                // Consume APPEND if present (REFRESH ... APPEND TO target)
16373                let _ = self.match_identifier("APPEND");
16374                None
16375            } else {
16376                Some(Box::new(self.parse_refresh_trigger_property()?))
16377            }
16378        } else {
16379            None
16380        };
16381
16382        // ClickHouse: TO destination_table after REFRESH ... APPEND
16383        // e.g., CREATE MATERIALIZED VIEW v REFRESH AFTER 1 SECOND APPEND TO tab (cols) EMPTY AS ...
16384        let to_table = if to_table.is_none() && self.match_token(TokenType::To) {
16385            Some(self.parse_table_ref()?)
16386        } else {
16387            to_table
16388        };
16389
16390        // ClickHouse: column definitions after REFRESH ... APPEND TO tab (cols)
16391        if schema.is_none()
16392            && self.check(TokenType::LParen)
16393            && matches!(
16394                self.config.dialect,
16395                Some(crate::dialects::DialectType::ClickHouse)
16396            )
16397        {
16398            let saved_pos = self.current;
16399            if let Some(Expression::Schema(parsed_schema)) = self.parse_schema()? {
16400                schema = Some(*parsed_schema);
16401            } else {
16402                self.current = saved_pos;
16403            }
16404        }
16405
16406        // Redshift: AUTO REFRESH YES|NO for materialized views
16407        let auto_refresh = if self.match_text_seq(&["AUTO", "REFRESH"]) {
16408            if self.match_identifier("YES") {
16409                Some(true)
16410            } else if self.match_identifier("NO") {
16411                Some(false)
16412            } else {
16413                None
16414            }
16415        } else {
16416            None
16417        };
16418
16419        // ClickHouse: Parse table properties (ENGINE, ORDER BY, SAMPLE, SETTINGS, TTL, etc.)
16420        // These appear after column definitions but before AS clause for materialized views
16421        let mut table_properties = Vec::new();
16422        if materialized
16423            && matches!(
16424                self.config.dialect,
16425                Some(crate::dialects::DialectType::ClickHouse)
16426            )
16427        {
16428            self.parse_clickhouse_table_properties(&mut table_properties)?;
16429        }
16430
16431        // ClickHouse: POPULATE / EMPTY keywords before AS in materialized views
16432        if materialized
16433            && matches!(
16434                self.config.dialect,
16435                Some(crate::dialects::DialectType::ClickHouse)
16436            )
16437        {
16438            let _ = self.match_identifier("POPULATE");
16439            let _ = self.match_identifier("EMPTY");
16440        }
16441
16442        // AS is optional - some dialects (e.g., Presto) allow SELECT without AS
16443        let has_as = self.match_token(TokenType::As);
16444        if !has_as && !self.check(TokenType::Select) && !self.check(TokenType::With) {
16445            // No AS and no SELECT/WITH means no query - return empty view (for partial statements)
16446            return Ok(Expression::CreateView(Box::new(CreateView {
16447                name,
16448                columns,
16449                query: Expression::Null(Null), // Placeholder for incomplete VIEW
16450                or_replace,
16451                or_alter,
16452                if_not_exists,
16453                materialized,
16454                temporary,
16455                secure,
16456                algorithm,
16457                definer,
16458                security,
16459                security_sql_style,
16460                security_after_name,
16461                query_parenthesized: false,
16462                locking_mode: None,
16463                locking_access: None,
16464                copy_grants,
16465                comment: view_comment,
16466                tags,
16467                options,
16468                build,
16469                refresh,
16470                schema: schema.map(Box::new),
16471                unique_key: unique_key.map(Box::new),
16472                no_schema_binding: false,
16473                auto_refresh,
16474                on_cluster,
16475                to_table,
16476                table_properties,
16477            })));
16478        }
16479
16480        // Parse Teradata LOCKING clause: LOCKING ROW|TABLE|DATABASE FOR ACCESS|READ|WRITE
16481        let mut locking_mode: Option<String> = None;
16482        let mut locking_access: Option<String> = None;
16483        if self.match_token(TokenType::Lock) || self.match_identifier("LOCKING") {
16484            // Capture: ROW, TABLE, DATABASE, etc.
16485            if self.match_token(TokenType::Row) {
16486                locking_mode = Some("ROW".to_string());
16487            } else if self.match_token(TokenType::Table) {
16488                locking_mode = Some("TABLE".to_string());
16489            } else if self.match_token(TokenType::Database) || self.match_identifier("DATABASE") {
16490                locking_mode = Some("DATABASE".to_string());
16491            }
16492            // Capture FOR ACCESS|READ|WRITE
16493            if self.match_token(TokenType::For) {
16494                if self.match_identifier("ACCESS") {
16495                    locking_access = Some("ACCESS".to_string());
16496                } else if self.match_identifier("READ") {
16497                    locking_access = Some("READ".to_string());
16498                } else if self.match_identifier("WRITE") {
16499                    locking_access = Some("WRITE".to_string());
16500                }
16501            }
16502        }
16503
16504        // Use parse_statement to handle SELECT, WITH...SELECT, or (SELECT...)
16505        let query_parenthesized = self.check(TokenType::LParen);
16506        let query = if self.check(TokenType::With) {
16507            self.parse_statement()?
16508        } else if query_parenthesized {
16509            // Handle (SELECT ...) or (WITH ... SELECT ...) - parenthesized query
16510            self.skip(); // consume (
16511            let inner = if self.check(TokenType::With) {
16512                self.parse_statement()?
16513            } else {
16514                self.parse_select()?
16515            };
16516            self.expect(TokenType::RParen)?;
16517            inner
16518        } else {
16519            self.parse_select()?
16520        };
16521
16522        // Redshift: WITH NO SCHEMA BINDING (after the query)
16523        let no_schema_binding = self.match_text_seq(&["WITH", "NO", "SCHEMA", "BINDING"]);
16524
16525        Ok(Expression::CreateView(Box::new(CreateView {
16526            name,
16527            columns,
16528            query,
16529            or_replace,
16530            or_alter,
16531            if_not_exists,
16532            materialized,
16533            temporary,
16534            secure,
16535            algorithm,
16536            definer,
16537            security,
16538            security_sql_style,
16539            security_after_name,
16540            query_parenthesized,
16541            locking_mode,
16542            locking_access,
16543            copy_grants,
16544            comment: view_comment,
16545            tags,
16546            options,
16547            build,
16548            refresh,
16549            schema: schema.map(Box::new),
16550            unique_key: unique_key.map(Box::new),
16551            no_schema_binding,
16552            auto_refresh,
16553            on_cluster,
16554            to_table,
16555            table_properties,
16556        })))
16557    }
16558
16559    /// Parse view column list: (col1, col2 OPTIONS(...) COMMENT 'text', ...)
16560    /// For simple view definitions without data types
16561    fn parse_view_columns(&mut self) -> Result<Vec<ViewColumn>> {
16562        self.expect(TokenType::LParen)?;
16563        let mut cols = Vec::new();
16564        loop {
16565            let col_name = self.expect_identifier()?;
16566            // BigQuery: OPTIONS (key=value, ...) on view column
16567            let options = if self.match_identifier("OPTIONS") {
16568                self.parse_options_list()?
16569            } else {
16570                Vec::new()
16571            };
16572            // Optional COMMENT 'text'
16573            let comment = if self.match_token(TokenType::Comment) {
16574                Some(self.expect_string()?)
16575            } else {
16576                None
16577            };
16578            cols.push(ViewColumn {
16579                name: Identifier::new(col_name),
16580                comment,
16581                options,
16582            });
16583            if !self.match_token(TokenType::Comma) {
16584                break;
16585            }
16586        }
16587        self.expect(TokenType::RParen)?;
16588        Ok(cols)
16589    }
16590
16591    /// Parse CREATE [CLUSTERED|NONCLUSTERED] INDEX
16592    fn parse_create_index_with_clustered(
16593        &mut self,
16594        unique: bool,
16595        clustered: Option<String>,
16596    ) -> Result<Expression> {
16597        self.expect(TokenType::Index)?;
16598
16599        // PostgreSQL: CREATE INDEX CONCURRENTLY idx ON t(c)
16600        let concurrently = self.match_identifier("CONCURRENTLY");
16601
16602        // Handle IF NOT EXISTS
16603        let if_not_exists =
16604            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
16605
16606        // Index name is optional when IF NOT EXISTS is specified (PostgreSQL)
16607        let name = if if_not_exists && self.check(TokenType::On) {
16608            Identifier::new("") // Empty name when omitted
16609        } else {
16610            self.expect_identifier_with_quoted()?
16611        };
16612        self.expect(TokenType::On)?;
16613        let table = self.parse_table_ref()?;
16614
16615        // Optional USING clause
16616        let using = if self.match_token(TokenType::Using) {
16617            Some(self.expect_identifier()?)
16618        } else {
16619            None
16620        };
16621
16622        // Parse index columns (optional for COLUMNSTORE indexes)
16623        let columns = if self.match_token(TokenType::LParen) {
16624            let cols = self.parse_index_columns()?;
16625            self.expect(TokenType::RParen)?;
16626            cols
16627        } else if clustered
16628            .as_ref()
16629            .is_some_and(|c| c.contains("COLUMNSTORE"))
16630        {
16631            // COLUMNSTORE indexes don't require a column list
16632            Vec::new()
16633        } else if matches!(
16634            self.config.dialect,
16635            Some(crate::dialects::DialectType::ClickHouse)
16636        ) {
16637            // ClickHouse: CREATE INDEX idx ON table expr TYPE minmax GRANULARITY 1
16638            // No parentheses around the expression — consume to semicolon as Command
16639            let mut parts = vec![
16640                "CREATE".to_string(),
16641                if unique {
16642                    "UNIQUE INDEX".to_string()
16643                } else {
16644                    "INDEX".to_string()
16645                },
16646                name.name.clone(),
16647                "ON".to_string(),
16648            ];
16649            // Rebuild table name
16650            if let Some(ref s) = table.schema {
16651                parts.push(format!("{}.{}", s.name, table.name.name));
16652            } else {
16653                parts.push(table.name.name.clone());
16654            }
16655            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
16656                let token = self.advance();
16657                if token.token_type == TokenType::String {
16658                    parts.push(format!("'{}'", token.text));
16659                } else if token.token_type == TokenType::QuotedIdentifier {
16660                    parts.push(format!("\"{}\"", token.text));
16661                } else {
16662                    parts.push(token.text.clone());
16663                }
16664            }
16665            return Ok(Expression::Command(Box::new(crate::expressions::Command {
16666                this: parts.join(" "),
16667            })));
16668        } else {
16669            self.expect(TokenType::LParen)?;
16670            let cols = self.parse_index_columns()?;
16671            self.expect(TokenType::RParen)?;
16672            cols
16673        };
16674
16675        // PostgreSQL: INCLUDE (col1, col2) clause
16676        let include_columns = if self.match_identifier("INCLUDE") {
16677            self.expect(TokenType::LParen)?;
16678            let mut cols = Vec::new();
16679            loop {
16680                cols.push(self.expect_identifier_with_quoted()?);
16681                if !self.match_token(TokenType::Comma) {
16682                    break;
16683                }
16684            }
16685            self.expect(TokenType::RParen)?;
16686            cols
16687        } else {
16688            Vec::new()
16689        };
16690
16691        // TSQL: WITH (option=value, ...) clause for index options
16692        let with_options = if self.check(TokenType::With) {
16693            // parse_with_properties expects the WITH keyword to NOT be consumed
16694            // but we need to check if we have WITH followed by LParen
16695            if self
16696                .peek_nth(1)
16697                .is_some_and(|t| t.token_type == TokenType::LParen)
16698            {
16699                self.skip(); // consume WITH
16700                self.parse_with_properties()?
16701            } else {
16702                Vec::new()
16703            }
16704        } else {
16705            Vec::new()
16706        };
16707
16708        // PostgreSQL: WHERE clause for partial indexes
16709        let where_clause = if self.match_token(TokenType::Where) {
16710            Some(Box::new(self.parse_expression()?))
16711        } else {
16712            None
16713        };
16714
16715        // TSQL: ON filegroup or partition scheme clause
16716        // e.g., ON PRIMARY, ON X([y])
16717        let on_filegroup = if self.match_token(TokenType::On) {
16718            // Get the filegroup/partition scheme name
16719            let token = self.advance();
16720            let mut filegroup = token.text.clone();
16721            // Check for partition scheme with column: ON partition_scheme(column)
16722            if self.match_token(TokenType::LParen) {
16723                filegroup.push('(');
16724                // Parse the partition column(s)
16725                loop {
16726                    let col_token = self.advance();
16727                    // For TSQL, use bracket quoting for quoted identifiers
16728                    if col_token.token_type == TokenType::QuotedIdentifier {
16729                        filegroup.push('[');
16730                        filegroup.push_str(&col_token.text);
16731                        filegroup.push(']');
16732                    } else {
16733                        filegroup.push_str(&col_token.text);
16734                    }
16735                    if !self.match_token(TokenType::Comma) {
16736                        break;
16737                    }
16738                    filegroup.push_str(", ");
16739                }
16740                self.expect(TokenType::RParen)?;
16741                filegroup.push(')');
16742            }
16743            Some(filegroup)
16744        } else {
16745            None
16746        };
16747
16748        Ok(Expression::CreateIndex(Box::new(CreateIndex {
16749            name,
16750            table,
16751            columns,
16752            unique,
16753            if_not_exists,
16754            using,
16755            clustered,
16756            concurrently,
16757            where_clause,
16758            include_columns,
16759            with_options,
16760            on_filegroup,
16761        })))
16762    }
16763
16764    /// Parse index columns - can be identifiers or expressions (like function calls)
16765    fn parse_index_columns(&mut self) -> Result<Vec<IndexColumn>> {
16766        let mut columns = Vec::new();
16767        loop {
16768            // Parse as expression to handle function calls like BOX(location, location)
16769            let expr = self.parse_expression()?;
16770
16771            // Extract column name from expression
16772            let column = match &expr {
16773                Expression::Identifier(ident) => ident.clone(),
16774                Expression::Column(col) => {
16775                    // For column expressions (e.g., simple identifier like [Col]),
16776                    // extract the identifier directly to preserve quoting
16777                    col.name.clone()
16778                }
16779                Expression::Function(_func) => {
16780                    // For function expressions, create an identifier from the function call
16781                    Identifier::new(self.expression_to_sql(&expr))
16782                }
16783                _ => Identifier::new(self.expression_to_sql(&expr)),
16784            };
16785
16786            // Parse optional PostgreSQL operator class (e.g., varchar_pattern_ops, public.gin_trgm_ops)
16787            // An opclass is an identifier that appears before ASC/DESC/NULLS and is not a keyword
16788            let opclass = if self.is_identifier_token()
16789                && !self.check(TokenType::Asc)
16790                && !self.check(TokenType::Desc)
16791                && !self.check(TokenType::Nulls)
16792            {
16793                let mut opclass_name = self.advance().text;
16794                // Handle qualified opclass names like public.gin_trgm_ops
16795                while self.match_token(TokenType::Dot) {
16796                    opclass_name.push('.');
16797                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
16798                        opclass_name.push_str(&self.advance().text);
16799                    }
16800                }
16801                Some(opclass_name)
16802            } else {
16803                None
16804            };
16805
16806            let desc = self.match_token(TokenType::Desc);
16807            let asc = if !desc {
16808                self.match_token(TokenType::Asc)
16809            } else {
16810                false
16811            };
16812            let nulls_first = if self.match_token(TokenType::Nulls) {
16813                if self.match_token(TokenType::First) {
16814                    Some(true)
16815                } else if self.match_token(TokenType::Last) {
16816                    Some(false)
16817                } else {
16818                    None
16819                }
16820            } else {
16821                None
16822            };
16823            columns.push(IndexColumn {
16824                column,
16825                desc,
16826                asc,
16827                nulls_first,
16828                opclass,
16829            });
16830            if !self.match_token(TokenType::Comma) {
16831                break;
16832            }
16833        }
16834        Ok(columns)
16835    }
16836
16837    /// Convert an expression to its SQL string representation (simple version for index expressions)
16838    fn expression_to_sql(&self, expr: &Expression) -> String {
16839        match expr {
16840            Expression::Identifier(ident) => ident.name.clone(),
16841            Expression::Function(func) => {
16842                let args = func
16843                    .args
16844                    .iter()
16845                    .map(|a| self.expression_to_sql(a))
16846                    .collect::<Vec<_>>()
16847                    .join(", ");
16848                format!("{}({})", func.name, args)
16849            }
16850            Expression::Column(col) => {
16851                if let Some(ref table) = col.table {
16852                    format!("{}.{}", table, col.name)
16853                } else {
16854                    col.name.to_string()
16855                }
16856            }
16857            Expression::Literal(lit) => match lit.as_ref() {
16858                Literal::String(s) => format!("'{}'", s),
16859                Literal::Number(n) => n.clone(),
16860                _ => "?".to_string(),
16861            },
16862            Expression::Null(_) => "NULL".to_string(),
16863            Expression::Boolean(b) => {
16864                if b.value {
16865                    "TRUE".to_string()
16866                } else {
16867                    "FALSE".to_string()
16868                }
16869            }
16870            _ => "?".to_string(),
16871        }
16872    }
16873
16874    /// Parse DROP statement
16875    fn parse_drop(&mut self) -> Result<Expression> {
16876        // Capture leading comments from the DROP token (e.g., "-- comment\nDROP TABLE ...")
16877        let leading_comments = self.current_leading_comments().to_vec();
16878        self.expect(TokenType::Drop)?;
16879
16880        // ClickHouse: DROP TEMPORARY TABLE / DROP TEMPORARY VIEW
16881        if self.check(TokenType::Temporary)
16882            && matches!(
16883                self.config.dialect,
16884                Some(crate::dialects::DialectType::ClickHouse)
16885            )
16886        {
16887            self.skip(); // consume TEMPORARY
16888            if self.check(TokenType::View) {
16889                return self.parse_drop_view(false);
16890            }
16891            return self.parse_drop_table_with_iceberg(leading_comments.clone(), false);
16892        }
16893
16894        // Snowflake: DROP ICEBERG TABLE
16895        if self.check_identifier("ICEBERG")
16896            && self.current + 1 < self.tokens.len()
16897            && self.tokens[self.current + 1].token_type == TokenType::Table
16898        {
16899            self.skip(); // consume ICEBERG
16900            return self.parse_drop_table_with_iceberg(leading_comments, true);
16901        }
16902
16903        match self.peek().token_type {
16904            TokenType::Table => self.parse_drop_table_with_iceberg(leading_comments, false),
16905            TokenType::View => self.parse_drop_view(false),
16906            TokenType::Materialized => {
16907                self.skip(); // consume MATERIALIZED
16908                self.parse_drop_view(true)
16909            }
16910            TokenType::Index => self.parse_drop_index(),
16911            TokenType::Schema => self.parse_drop_schema(),
16912            TokenType::Database => self.parse_drop_database(),
16913            TokenType::Function => self.parse_drop_function(),
16914            TokenType::Procedure => self.parse_drop_procedure(),
16915            TokenType::Sequence => self.parse_drop_sequence(),
16916            TokenType::Trigger => self.parse_drop_trigger(),
16917            TokenType::Type => self.parse_drop_type(),
16918            TokenType::Domain => {
16919                // DROP DOMAIN is similar to DROP TYPE
16920                self.skip();
16921                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16922                let name = self.parse_table_ref()?;
16923                let cascade = self.match_token(TokenType::Cascade);
16924                if !cascade {
16925                    self.match_token(TokenType::Restrict);
16926                }
16927                Ok(Expression::DropType(Box::new(DropType {
16928                    name,
16929                    if_exists,
16930                    cascade,
16931                })))
16932            }
16933            TokenType::Namespace => {
16934                // DROP NAMESPACE is similar to DROP SCHEMA (Spark/Databricks)
16935                self.skip();
16936                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16937                // Parse potentially qualified namespace name (a.b.c)
16938                let mut name_parts = vec![self.expect_identifier()?];
16939                while self.match_token(TokenType::Dot) {
16940                    name_parts.push(self.expect_identifier()?);
16941                }
16942                let name = Identifier::new(name_parts.join("."));
16943                let cascade = self.match_token(TokenType::Cascade);
16944                if !cascade {
16945                    self.match_token(TokenType::Restrict);
16946                }
16947                Ok(Expression::DropNamespace(Box::new(DropNamespace {
16948                    name,
16949                    if_exists,
16950                    cascade,
16951                })))
16952            }
16953            _ => {
16954                // ClickHouse: DROP DICTIONARY, DROP USER, DROP QUOTA, DROP ROLE,
16955                // DROP ROW POLICY, DROP SETTINGS PROFILE, DROP NAMED COLLECTION
16956                if matches!(
16957                    self.config.dialect,
16958                    Some(crate::dialects::DialectType::ClickHouse)
16959                ) {
16960                    let text_upper = self.peek().text.to_ascii_uppercase();
16961                    if matches!(
16962                        text_upper.as_str(),
16963                        "DICTIONARY"
16964                            | "USER"
16965                            | "QUOTA"
16966                            | "ROLE"
16967                            | "ROW"
16968                            | "POLICY"
16969                            | "NAMED"
16970                            | "WORKLOAD"
16971                            | "RESOURCE"
16972                            | "PROFILE"
16973                    ) || self.check(TokenType::Settings)
16974                        || self.check(TokenType::Partition)
16975                    {
16976                        self.skip(); // consume keyword, previous() is now set
16977                        let mut tokens: Vec<(String, TokenType)> = vec![
16978                            ("DROP".to_string(), TokenType::Var),
16979                            (
16980                                self.previous().text.to_ascii_uppercase(),
16981                                self.previous().token_type,
16982                            ),
16983                        ];
16984                        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
16985                            let token = self.advance();
16986                            let text = if token.token_type == TokenType::QuotedIdentifier {
16987                                format!("\"{}\"", token.text)
16988                            } else if token.token_type == TokenType::String {
16989                                format!("'{}'", token.text)
16990                            } else {
16991                                token.text.clone()
16992                            };
16993                            tokens.push((text, token.token_type));
16994                        }
16995                        return Ok(Expression::Command(Box::new(Command {
16996                            this: self.join_command_tokens(tokens),
16997                        })));
16998                    }
16999                }
17000                // Snowflake: DROP STREAM, DROP TASK, DROP STAGE, DROP WAREHOUSE, etc.
17001                if matches!(
17002                    self.config.dialect,
17003                    Some(crate::dialects::DialectType::Snowflake)
17004                ) {
17005                    let text_upper = self.peek().text.to_ascii_uppercase();
17006                    let is_snowflake_drop = matches!(
17007                        text_upper.as_str(),
17008                        "STREAM"
17009                            | "TASK"
17010                            | "STAGE"
17011                            | "WAREHOUSE"
17012                            | "PIPE"
17013                            | "INTEGRATION"
17014                            | "TAG"
17015                            | "NETWORK"
17016                            | "SHARE"
17017                    ) || (text_upper == "FILE"
17018                        && self.current + 1 < self.tokens.len()
17019                        && self.tokens[self.current + 1]
17020                            .text
17021                            .eq_ignore_ascii_case("FORMAT"));
17022                    if is_snowflake_drop {
17023                        self.skip(); // consume the object type keyword
17024                        let mut tokens: Vec<(String, TokenType)> = vec![
17025                            ("DROP".to_string(), TokenType::Var),
17026                            (
17027                                self.previous().text.to_ascii_uppercase(),
17028                                self.previous().token_type,
17029                            ),
17030                        ];
17031                        // For FILE FORMAT, also consume FORMAT
17032                        if text_upper == "FILE" {
17033                            let fmt = self.advance();
17034                            tokens.push((fmt.text.to_ascii_uppercase(), fmt.token_type));
17035                        }
17036                        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17037                            let token = self.advance();
17038                            let text = if token.token_type == TokenType::QuotedIdentifier {
17039                                format!("\"{}\"", token.text)
17040                            } else if token.token_type == TokenType::String {
17041                                format!("'{}'", token.text)
17042                            } else {
17043                                token.text.clone()
17044                            };
17045                            tokens.push((text, token.token_type));
17046                        }
17047                        return Ok(Expression::Command(Box::new(Command {
17048                            this: self.join_command_tokens(tokens),
17049                        })));
17050                    }
17051                }
17052                Err(self.parse_error(format!(
17053                    "Expected TABLE, VIEW, INDEX, SCHEMA, DATABASE, FUNCTION, PROCEDURE, SEQUENCE, TRIGGER, TYPE, or NAMESPACE after DROP, got {:?}",
17054                    self.peek().token_type
17055                )))
17056            }
17057        }
17058    }
17059
17060    /// Parse DROP TABLE
17061    fn parse_drop_table_with_iceberg(
17062        &mut self,
17063        leading_comments: Vec<String>,
17064        iceberg: bool,
17065    ) -> Result<Expression> {
17066        self.expect(TokenType::Table)?;
17067
17068        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17069
17070        // ClickHouse: IF EMPTY
17071        if !if_exists
17072            && matches!(
17073                self.config.dialect,
17074                Some(crate::dialects::DialectType::ClickHouse)
17075            )
17076        {
17077            if self.check(TokenType::If)
17078                && self.current + 1 < self.tokens.len()
17079                && self.tokens[self.current + 1]
17080                    .text
17081                    .eq_ignore_ascii_case("EMPTY")
17082            {
17083                self.skip(); // consume IF
17084                self.skip(); // consume EMPTY
17085            }
17086        }
17087
17088        // Parse table names (can be multiple)
17089        let mut names = Vec::new();
17090        loop {
17091            names.push(self.parse_table_ref()?);
17092            if !self.match_token(TokenType::Comma) {
17093                break;
17094            }
17095        }
17096
17097        // Handle CASCADE [CONSTRAINTS] or RESTRICT
17098        let mut cascade = false;
17099        let mut cascade_constraints = false;
17100        let mut restrict = false;
17101        if self.match_token(TokenType::Cascade) {
17102            if self.match_identifier("CONSTRAINTS") {
17103                cascade_constraints = true;
17104            } else {
17105                cascade = true;
17106            }
17107        } else {
17108            restrict = self.match_token(TokenType::Restrict);
17109        }
17110
17111        // Handle PURGE (Oracle)
17112        let purge = self.match_identifier("PURGE");
17113
17114        // ClickHouse: ON CLUSTER clause
17115        if matches!(
17116            self.config.dialect,
17117            Some(crate::dialects::DialectType::ClickHouse)
17118        ) {
17119            let _ = self.parse_on_cluster_clause()?;
17120        }
17121
17122        // ClickHouse: SYNC keyword
17123        let sync = if matches!(
17124            self.config.dialect,
17125            Some(crate::dialects::DialectType::ClickHouse)
17126        ) {
17127            let s = self.match_identifier("SYNC");
17128            self.match_identifier("NO");
17129            self.match_identifier("DELAY");
17130            s
17131        } else {
17132            false
17133        };
17134
17135        Ok(Expression::DropTable(Box::new(DropTable {
17136            names,
17137            if_exists,
17138            cascade,
17139            cascade_constraints,
17140            purge,
17141            leading_comments,
17142            object_id_args: None,
17143            sync,
17144            iceberg,
17145            restrict,
17146        })))
17147    }
17148
17149    /// Parse DROP VIEW
17150    fn parse_drop_view(&mut self, materialized: bool) -> Result<Expression> {
17151        self.expect(TokenType::View)?;
17152
17153        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17154        let name = self.parse_table_ref()?;
17155
17156        // ClickHouse: ON CLUSTER clause
17157        if matches!(
17158            self.config.dialect,
17159            Some(crate::dialects::DialectType::ClickHouse)
17160        ) {
17161            let _ = self.parse_on_cluster_clause()?;
17162            self.match_identifier("SYNC");
17163        }
17164
17165        Ok(Expression::DropView(Box::new(DropView {
17166            name,
17167            if_exists,
17168            materialized,
17169        })))
17170    }
17171
17172    /// Parse DROP INDEX
17173    fn parse_drop_index(&mut self) -> Result<Expression> {
17174        self.expect(TokenType::Index)?;
17175
17176        // PostgreSQL CONCURRENTLY modifier
17177        let concurrently = self.match_identifier("CONCURRENTLY");
17178
17179        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17180
17181        // Parse potentially qualified index name (a.b.c)
17182        let mut name_parts = vec![self.expect_identifier()?];
17183        while self.match_token(TokenType::Dot) {
17184            name_parts.push(self.expect_identifier()?);
17185        }
17186        let name = Identifier::new(name_parts.join("."));
17187
17188        // Optional ON table
17189        let table = if self.match_token(TokenType::On) {
17190            Some(self.parse_table_ref()?)
17191        } else {
17192            None
17193        };
17194
17195        Ok(Expression::DropIndex(Box::new(DropIndex {
17196            name,
17197            table,
17198            if_exists,
17199            concurrently,
17200        })))
17201    }
17202
17203    /// Parse ALTER statement
17204    fn parse_alter(&mut self) -> Result<Expression> {
17205        self.expect(TokenType::Alter)?;
17206
17207        // Check for ICEBERG modifier before TABLE
17208        let alter_table_modifier = if self.check_identifier("ICEBERG") {
17209            self.skip();
17210            Some("ICEBERG".to_string())
17211        } else {
17212            None
17213        };
17214
17215        match self.peek().token_type {
17216            TokenType::Table => {
17217                self.skip();
17218                // Handle IF EXISTS after ALTER TABLE
17219                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17220                // Handle PostgreSQL ONLY modifier: ALTER TABLE ONLY "Album" ...
17221                let has_only = self.match_token(TokenType::Only);
17222                let mut name = self.parse_table_ref()?;
17223                if has_only {
17224                    name.only = true;
17225                }
17226
17227                // ClickHouse: ON CLUSTER clause
17228                let on_cluster = self.parse_on_cluster_clause()?;
17229
17230                // Hive: PARTITION(key=value, ...) clause before actions
17231                let partition = if self.match_token(TokenType::Partition) {
17232                    self.expect(TokenType::LParen)?;
17233                    let mut parts = Vec::new();
17234                    loop {
17235                        let key = self.expect_identifier()?;
17236                        self.expect(TokenType::Eq)?;
17237                        let value = self.parse_expression()?;
17238                        parts.push((Identifier::new(key), value));
17239                        if !self.match_token(TokenType::Comma) {
17240                            break;
17241                        }
17242                    }
17243                    self.expect(TokenType::RParen)?;
17244                    Some(parts)
17245                } else {
17246                    None
17247                };
17248
17249                let mut actions = Vec::new();
17250                let mut last_was_add_column = false;
17251                let mut with_check_modifier: Option<String> = None;
17252
17253                loop {
17254                    // Check for MySQL trailing options (ALGORITHM=val, LOCK=val)
17255                    // before trying to parse as a column def or action.
17256                    // The comma before ALGORITHM was consumed at the bottom of the previous iteration.
17257                    if self.check_identifier("ALGORITHM") || self.check_identifier("LOCK") {
17258                        break;
17259                    }
17260
17261                    // TSQL: WITH CHECK / WITH NOCHECK before ADD CONSTRAINT
17262                    if self.check(TokenType::With) {
17263                        let saved = self.current;
17264                        self.skip(); // consume WITH
17265                        if self.check(TokenType::Check) {
17266                            self.skip(); // consume CHECK
17267                            with_check_modifier = Some("WITH CHECK".to_string());
17268                            // Continue to parse the actual action (ADD CONSTRAINT, etc.)
17269                        } else if self.check_identifier("NOCHECK") {
17270                            self.skip(); // consume NOCHECK
17271                            with_check_modifier = Some("WITH NOCHECK".to_string());
17272                            // Continue to parse the actual action (ADD CONSTRAINT, etc.)
17273                        } else {
17274                            // Not WITH CHECK/NOCHECK, restore position
17275                            self.current = saved;
17276                        }
17277                    }
17278
17279                    // If last action was ADD COLUMN and we just saw a comma,
17280                    // check if this is another column definition (not a new action keyword)
17281                    if last_was_add_column
17282                        && !self.check(TokenType::Add)
17283                        && !self.check(TokenType::Drop)
17284                        && !self.check(TokenType::Alter)
17285                        && !self.check(TokenType::Rename)
17286                        && !self.check(TokenType::Set)
17287                        && !self.check_identifier("MODIFY")
17288                        && !self.check(TokenType::Delete)
17289                        && !self.check(TokenType::Update)
17290                        && !self.check_identifier("DETACH")
17291                        && !self.check_identifier("ATTACH")
17292                        && !self.check_identifier("FREEZE")
17293                        && !self.check_identifier("CLEAR")
17294                        && !self.check_identifier("MATERIALIZE")
17295                        && !self.check(TokenType::Comment)
17296                        && !self.check(TokenType::Replace)
17297                        && !self.check_identifier("MOVE")
17298                        && !self.check_identifier("REMOVE")
17299                        && !self.check_identifier("APPLY")
17300                    {
17301                        // Parse additional column definition
17302                        self.match_token(TokenType::Column); // optional COLUMN keyword
17303                        let if_not_exists = self.match_keywords(&[
17304                            TokenType::If,
17305                            TokenType::Not,
17306                            TokenType::Exists,
17307                        ]);
17308                        let col_def = self.parse_column_def()?;
17309                        let position = if self.match_token(TokenType::First) {
17310                            Some(ColumnPosition::First)
17311                        } else if self.match_token(TokenType::After) {
17312                            let after_col = self.expect_identifier()?;
17313                            // ClickHouse: AFTER n.a (dotted nested column name)
17314                            let after_name = if self.match_token(TokenType::Dot) {
17315                                let field = self.expect_identifier()?;
17316                                format!("{}.{}", after_col, field)
17317                            } else {
17318                                after_col
17319                            };
17320                            Some(ColumnPosition::After(Identifier::new(after_name)))
17321                        } else {
17322                            None
17323                        };
17324                        actions.push(AlterTableAction::AddColumn {
17325                            column: col_def,
17326                            if_not_exists,
17327                            position,
17328                        });
17329                        // last_was_add_column remains true
17330                    } else {
17331                        // Check for MySQL trailing options (ALGORITHM=val, LOCK=val)
17332                        // before trying to parse as an action
17333                        if self.check_identifier("ALGORITHM") || self.check_identifier("LOCK") {
17334                            // Retreat one to re-process the comma in the trailing options loop
17335                            self.current -= 1; // back up past the comma consumed in loop
17336                            break;
17337                        }
17338                        let action = self.parse_alter_action()?;
17339                        last_was_add_column = matches!(action, AlterTableAction::AddColumn { .. });
17340                        actions.push(action);
17341                    }
17342                    if !self.match_token(TokenType::Comma) {
17343                        break;
17344                    }
17345                }
17346
17347                // Parse trailing MySQL ALTER TABLE options: ALGORITHM=val, LOCK=val
17348                // These can appear after actions separated by commas (comma already consumed)
17349                // or directly if no actions were parsed
17350                let mut algorithm = None;
17351                let mut lock = None;
17352                loop {
17353                    // First check without consuming comma (comma may have been consumed by action loop)
17354                    if self.check_identifier("ALGORITHM") {
17355                        self.skip();
17356                        self.expect(TokenType::Eq)?;
17357                        algorithm = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17358                        self.match_token(TokenType::Comma); // optional trailing comma
17359                    } else if self.check_identifier("LOCK") {
17360                        self.skip();
17361                        self.expect(TokenType::Eq)?;
17362                        lock = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17363                        self.match_token(TokenType::Comma); // optional trailing comma
17364                    } else if self.match_token(TokenType::Comma) {
17365                        // Try after comma
17366                        if self.check_identifier("ALGORITHM") {
17367                            self.skip();
17368                            self.expect(TokenType::Eq)?;
17369                            algorithm =
17370                                Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17371                        } else if self.check_identifier("LOCK") {
17372                            self.skip();
17373                            self.expect(TokenType::Eq)?;
17374                            lock = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17375                        } else {
17376                            self.current -= 1;
17377                            break;
17378                        }
17379                    } else {
17380                        break;
17381                    }
17382                }
17383
17384                // ClickHouse: consume optional trailing SETTINGS clause
17385                // e.g., ALTER TABLE t ADD COLUMN c Int64 SETTINGS mutations_sync=2, alter_sync=2
17386                if matches!(
17387                    self.config.dialect,
17388                    Some(crate::dialects::DialectType::ClickHouse)
17389                ) && self.check(TokenType::Settings)
17390                {
17391                    self.skip(); // consume SETTINGS
17392                    let _ = self.parse_settings_property()?;
17393                }
17394
17395                Ok(Expression::AlterTable(Box::new(AlterTable {
17396                    name,
17397                    actions,
17398                    if_exists,
17399                    algorithm,
17400                    lock,
17401                    with_check: with_check_modifier,
17402                    partition,
17403                    on_cluster,
17404                    table_modifier: alter_table_modifier,
17405                })))
17406            }
17407            TokenType::View => self.parse_alter_view_with_modifiers(None, None, None),
17408            TokenType::Index => self.parse_alter_index(),
17409            TokenType::Sequence => self.parse_alter_sequence(),
17410            _ if self.check_identifier("SESSION") => {
17411                // ALTER SESSION SET/UNSET (Snowflake)
17412                self.skip(); // consume SESSION
17413                match self.parse_alter_session()? {
17414                    Some(expr) => Ok(expr),
17415                    None => {
17416                        // Fall back to command
17417                        Ok(Expression::Command(Box::new(Command {
17418                            this: "ALTER SESSION".to_string(),
17419                        })))
17420                    }
17421                }
17422            }
17423            _ => {
17424                // MySQL: ALTER ALGORITHM = val VIEW, ALTER DEFINER = val VIEW,
17425                // ALTER SQL SECURITY = val VIEW
17426                let mut view_algorithm = None;
17427                let mut view_definer = None;
17428                let mut view_sql_security = None;
17429
17430                loop {
17431                    if self.check_identifier("ALGORITHM") {
17432                        self.skip();
17433                        self.expect(TokenType::Eq)?;
17434                        view_algorithm =
17435                            Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17436                    } else if self.check_identifier("DEFINER") {
17437                        self.skip();
17438                        self.expect(TokenType::Eq)?;
17439                        // Parse user@host format: 'admin'@'localhost'
17440                        let mut definer_str = String::new();
17441                        if self.check(TokenType::String) {
17442                            definer_str.push_str(&format!("'{}'", self.advance().text));
17443                        } else {
17444                            definer_str.push_str(&self.expect_identifier_or_keyword()?);
17445                        }
17446                        // Check for @ separator
17447                        if !self.is_at_end() && self.peek().text == "@" {
17448                            definer_str.push_str(&self.advance().text);
17449                            if self.check(TokenType::String) {
17450                                definer_str.push_str(&format!("'{}'", self.advance().text));
17451                            } else if !self.is_at_end() {
17452                                definer_str.push_str(&self.advance().text);
17453                            }
17454                        }
17455                        view_definer = Some(definer_str);
17456                    } else if self.check_identifier("SQL") {
17457                        self.skip();
17458                        if self.match_identifier("SECURITY") {
17459                            self.match_token(TokenType::Eq);
17460                            view_sql_security =
17461                                Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17462                        }
17463                    } else {
17464                        break;
17465                    }
17466                }
17467
17468                if self.check(TokenType::View) {
17469                    self.parse_alter_view_with_modifiers(
17470                        view_algorithm,
17471                        view_definer,
17472                        view_sql_security,
17473                    )
17474                } else {
17475                    // Fall back to Raw for unrecognized ALTER targets
17476                    let start = self.current;
17477                    while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17478                        self.skip();
17479                    }
17480                    let sql = self.tokens_to_sql(start, self.current);
17481                    Ok(Expression::Raw(Raw {
17482                        sql: format!("ALTER {}", sql),
17483                    }))
17484                }
17485            }
17486        }
17487    }
17488
17489    /// Parse ALTER TABLE action
17490    fn parse_alter_action(&mut self) -> Result<AlterTableAction> {
17491        if self.match_token(TokenType::Add) {
17492            // ClickHouse: ADD INDEX idx expr TYPE minmax GRANULARITY 1
17493            // ClickHouse: ADD PROJECTION name (SELECT ...)
17494            // ClickHouse: ADD STATISTICS col1, col2 TYPE tdigest, uniq
17495            // These have different syntax from MySQL ADD INDEX, so consume as Raw
17496            if matches!(
17497                self.config.dialect,
17498                Some(crate::dialects::DialectType::ClickHouse)
17499            ) && (self.check(TokenType::Index)
17500                || self.check_identifier("PROJECTION")
17501                || self.check_identifier("STATISTICS"))
17502            {
17503                let is_statistics = self.check_identifier("STATISTICS");
17504                let mut tokens: Vec<(String, TokenType)> =
17505                    vec![("ADD".to_string(), TokenType::Add)];
17506                let mut paren_depth = 0i32;
17507                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17508                    // STATISTICS uses commas internally (col1, col2 TYPE t1, t2), don't break at comma
17509                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_statistics {
17510                        break;
17511                    }
17512                    let token = self.advance();
17513                    if token.token_type == TokenType::LParen {
17514                        paren_depth += 1;
17515                    }
17516                    if token.token_type == TokenType::RParen {
17517                        paren_depth -= 1;
17518                    }
17519                    let text = if token.token_type == TokenType::QuotedIdentifier {
17520                        format!("\"{}\"", token.text)
17521                    } else if token.token_type == TokenType::String {
17522                        format!("'{}'", token.text)
17523                    } else {
17524                        token.text.clone()
17525                    };
17526                    tokens.push((text, token.token_type));
17527                }
17528                return Ok(AlterTableAction::Raw {
17529                    sql: self.join_command_tokens(tokens),
17530                });
17531            }
17532            // ADD CONSTRAINT or ADD COLUMN or ADD INDEX
17533            if self.match_token(TokenType::Constraint) {
17534                // ADD CONSTRAINT name ...
17535                let name = Some(self.expect_identifier_with_quoted()?);
17536                let constraint = self.parse_constraint_definition(name)?;
17537                Ok(AlterTableAction::AddConstraint(constraint))
17538            } else if self.check(TokenType::PrimaryKey)
17539                || self.check(TokenType::ForeignKey)
17540                || self.check(TokenType::Check)
17541            {
17542                // ADD PRIMARY KEY / FOREIGN KEY / CHECK (without CONSTRAINT keyword)
17543                let constraint = self.parse_table_constraint()?;
17544                Ok(AlterTableAction::AddConstraint(constraint))
17545            } else if self.check(TokenType::Index)
17546                || self.check(TokenType::Key)
17547                || self.check(TokenType::Unique)
17548                || self.check_identifier("FULLTEXT")
17549                || self.check_identifier("SPATIAL")
17550            {
17551                // ADD [UNIQUE|FULLTEXT|SPATIAL] [{INDEX|KEY}] [name] (columns) [USING {BTREE|HASH}]
17552                let kind = if self.match_token(TokenType::Unique) {
17553                    Some("UNIQUE".to_string())
17554                } else if self.match_identifier("FULLTEXT") {
17555                    Some("FULLTEXT".to_string())
17556                } else if self.match_identifier("SPATIAL") {
17557                    Some("SPATIAL".to_string())
17558                } else {
17559                    None
17560                };
17561                // Consume optional INDEX or KEY keyword, track which was used
17562                let use_key_keyword = if self.match_token(TokenType::Key) {
17563                    true
17564                } else {
17565                    self.match_token(TokenType::Index);
17566                    false
17567                };
17568
17569                // Optional index name (before the columns)
17570                let name = if !self.check(TokenType::LParen) && !self.check(TokenType::Using) {
17571                    Some(self.expect_identifier_with_quoted()?)
17572                } else {
17573                    None
17574                };
17575
17576                // Parse columns (with optional prefix length and DESC)
17577                self.expect(TokenType::LParen)?;
17578                let columns = self.parse_index_identifier_list()?;
17579                self.expect(TokenType::RParen)?;
17580
17581                // Parse optional USING BTREE|HASH
17582                let modifiers = self.parse_constraint_modifiers();
17583
17584                Ok(AlterTableAction::AddConstraint(TableConstraint::Index {
17585                    name,
17586                    columns,
17587                    kind,
17588                    modifiers,
17589                    use_key_keyword,
17590                    expression: None,
17591                    index_type: None,
17592                    granularity: None,
17593                }))
17594            } else if self.match_identifier("COLUMNS") {
17595                // ADD COLUMNS (col1 TYPE, col2 TYPE, ...) [CASCADE] - Hive/Spark syntax
17596                self.expect(TokenType::LParen)?;
17597                let mut columns = Vec::new();
17598                loop {
17599                    let col_def = self.parse_column_def()?;
17600                    columns.push(col_def);
17601                    if !self.match_token(TokenType::Comma) {
17602                        break;
17603                    }
17604                }
17605                self.expect(TokenType::RParen)?;
17606                let cascade = self.match_token(TokenType::Cascade);
17607                Ok(AlterTableAction::AddColumns { columns, cascade })
17608            } else if self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]) {
17609                // ADD IF NOT EXISTS PARTITION(key = value) - Hive/Spark syntax
17610                // ADD IF NOT EXISTS col1 INT, col2 INT - Snowflake syntax
17611                if self.match_token(TokenType::Partition) {
17612                    self.expect(TokenType::LParen)?;
17613                    let mut partition_exprs = Vec::new();
17614                    loop {
17615                        if let Some(expr) = self.parse_conjunction()? {
17616                            partition_exprs.push(expr);
17617                        }
17618                        if !self.match_token(TokenType::Comma) {
17619                            break;
17620                        }
17621                    }
17622                    self.expect(TokenType::RParen)?;
17623                    let partition =
17624                        Expression::Partition(Box::new(crate::expressions::Partition {
17625                            expressions: partition_exprs,
17626                            subpartition: false,
17627                        }));
17628                    let location = if self.match_text_seq(&["LOCATION"]) {
17629                        self.parse_property()?
17630                    } else {
17631                        None
17632                    };
17633                    return Ok(AlterTableAction::AddPartition {
17634                        partition,
17635                        if_not_exists: true,
17636                        location,
17637                    });
17638                } else {
17639                    // Snowflake: ADD IF NOT EXISTS col1 INT, [IF NOT EXISTS] col2 INT
17640                    // Parse just the first column; the caller's comma loop handles the rest
17641                    let col_def = self.parse_column_def()?;
17642                    return Ok(AlterTableAction::AddColumn {
17643                        column: col_def,
17644                        if_not_exists: true,
17645                        position: None,
17646                    });
17647                }
17648            } else if self.check(TokenType::Partition) {
17649                // ADD PARTITION(key = value) - Hive/Spark syntax
17650                self.skip(); // consume PARTITION
17651                self.expect(TokenType::LParen)?;
17652                let mut partition_exprs = Vec::new();
17653                loop {
17654                    if let Some(expr) = self.parse_conjunction()? {
17655                        partition_exprs.push(expr);
17656                    }
17657                    if !self.match_token(TokenType::Comma) {
17658                        break;
17659                    }
17660                }
17661                self.expect(TokenType::RParen)?;
17662                let partition = Expression::Partition(Box::new(crate::expressions::Partition {
17663                    expressions: partition_exprs,
17664                    subpartition: false,
17665                }));
17666                let location = if self.match_text_seq(&["LOCATION"]) {
17667                    // Parse the LOCATION value (typically a string literal like 'path')
17668                    Some(self.parse_primary()?)
17669                } else {
17670                    None
17671                };
17672                Ok(AlterTableAction::AddPartition {
17673                    partition,
17674                    if_not_exists: false,
17675                    location,
17676                })
17677            } else {
17678                // ADD COLUMN or ADD (col1 TYPE, col2 TYPE) for Oracle
17679                let has_column_keyword = self.match_token(TokenType::Column); // optional COLUMN keyword
17680
17681                // Check for Oracle-style ADD (col1 TYPE, col2 TYPE, ...) without COLUMN keyword
17682                if !has_column_keyword && self.check(TokenType::LParen) {
17683                    // Oracle multi-column ADD syntax: ADD (col1 TYPE, col2 TYPE, ...)
17684                    self.skip(); // consume '('
17685                    let mut columns = Vec::new();
17686                    loop {
17687                        let col_def = self.parse_column_def()?;
17688                        columns.push(col_def);
17689                        if !self.match_token(TokenType::Comma) {
17690                            break;
17691                        }
17692                    }
17693                    self.expect(TokenType::RParen)?;
17694                    // Use AddColumns with cascade=false for Oracle syntax
17695                    Ok(AlterTableAction::AddColumns {
17696                        columns,
17697                        cascade: false,
17698                    })
17699                } else {
17700                    // Handle IF NOT EXISTS for ADD COLUMN
17701                    let if_not_exists =
17702                        self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
17703                    let col_def = self.parse_column_def()?;
17704                    // Check for FIRST or AFTER position modifiers (MySQL/MariaDB)
17705                    let position = if self.match_token(TokenType::First) {
17706                        Some(ColumnPosition::First)
17707                    } else if self.match_token(TokenType::After) {
17708                        let after_col = self.expect_identifier()?;
17709                        // ClickHouse: AFTER n.a (dotted nested column name)
17710                        let after_name = if self.match_token(TokenType::Dot) {
17711                            let field = self.expect_identifier()?;
17712                            format!("{}.{}", after_col, field)
17713                        } else {
17714                            after_col
17715                        };
17716                        Some(ColumnPosition::After(Identifier::new(after_name)))
17717                    } else {
17718                        None
17719                    };
17720                    Ok(AlterTableAction::AddColumn {
17721                        column: col_def,
17722                        if_not_exists,
17723                        position,
17724                    })
17725                }
17726            }
17727        } else if self.match_token(TokenType::Drop) {
17728            // ClickHouse: DROP INDEX idx, DROP PROJECTION name, DROP STATISTICS, etc.
17729            // These have different syntax from MySQL, so consume as Raw
17730            if matches!(
17731                self.config.dialect,
17732                Some(crate::dialects::DialectType::ClickHouse)
17733            ) && (self.check(TokenType::Index)
17734                || self.check_identifier("PROJECTION")
17735                || self.check_identifier("STATISTICS")
17736                || self.check_identifier("DETACHED")
17737                || self.check_identifier("PART"))
17738            {
17739                let is_statistics = self.check_identifier("STATISTICS");
17740                let mut tokens: Vec<(String, TokenType)> =
17741                    vec![("DROP".to_string(), TokenType::Drop)];
17742                let mut paren_depth = 0i32;
17743                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17744                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_statistics {
17745                        break;
17746                    }
17747                    let token = self.advance();
17748                    if token.token_type == TokenType::LParen {
17749                        paren_depth += 1;
17750                    }
17751                    if token.token_type == TokenType::RParen {
17752                        paren_depth -= 1;
17753                    }
17754                    let text = if token.token_type == TokenType::QuotedIdentifier {
17755                        format!("\"{}\"", token.text)
17756                    } else if token.token_type == TokenType::String {
17757                        format!("'{}'", token.text)
17758                    } else {
17759                        token.text.clone()
17760                    };
17761                    tokens.push((text, token.token_type));
17762                }
17763                return Ok(AlterTableAction::Raw {
17764                    sql: self.join_command_tokens(tokens),
17765                });
17766            }
17767            // Handle IF EXISTS before determining what to drop
17768            let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17769
17770            if self.match_token(TokenType::Partition) {
17771                // DROP [IF EXISTS] PARTITION expr [, PARTITION expr ...]
17772                // ClickHouse supports: PARTITION 201901, PARTITION ALL,
17773                // PARTITION tuple(...), PARTITION ID '...'
17774                let mut partitions = Vec::new();
17775                loop {
17776                    if self.check(TokenType::LParen) {
17777                        // ClickHouse: PARTITION (expr) or PARTITION (expr, expr, ...)
17778                        // Standard SQL: PARTITION (key=value, ...)
17779                        // Peek ahead: if LParen is followed by String/Number (not identifier=),
17780                        // parse as expression
17781                        let is_ch_expr = matches!(
17782                            self.config.dialect,
17783                            Some(crate::dialects::DialectType::ClickHouse)
17784                        ) && self.current + 1 < self.tokens.len()
17785                            && (self.tokens[self.current + 1].token_type == TokenType::String
17786                                || self.tokens[self.current + 1].token_type == TokenType::Number
17787                                || self.tokens[self.current + 1].token_type == TokenType::LParen
17788                                || (self.current + 2 < self.tokens.len()
17789                                    && self.tokens[self.current + 2].token_type != TokenType::Eq));
17790                        if is_ch_expr {
17791                            // Parse as tuple expression
17792                            let expr = self.parse_expression()?;
17793                            partitions.push(vec![(Identifier::new("__expr__".to_string()), expr)]);
17794                        } else {
17795                            self.skip(); // consume (
17796                            let mut parts = Vec::new();
17797                            loop {
17798                                let key = self.expect_identifier()?;
17799                                self.expect(TokenType::Eq)?;
17800                                let value = self.parse_expression()?;
17801                                parts.push((Identifier::new(key), value));
17802                                if !self.match_token(TokenType::Comma) {
17803                                    break;
17804                                }
17805                            }
17806                            self.expect(TokenType::RParen)?;
17807                            partitions.push(parts);
17808                        }
17809                    } else if self.match_text_seq(&["ALL"]) {
17810                        // ClickHouse: PARTITION ALL
17811                        partitions.push(vec![(
17812                            Identifier::new("ALL".to_string()),
17813                            Expression::Boolean(BooleanLiteral { value: true }),
17814                        )]);
17815                    } else if self.match_text_seq(&["ID"]) {
17816                        // ClickHouse: PARTITION ID 'string'
17817                        let id_val = self.parse_expression()?;
17818                        partitions.push(vec![(Identifier::new("ID".to_string()), id_val)]);
17819                    } else {
17820                        // ClickHouse: PARTITION <expression> (number, tuple(...), etc.)
17821                        let expr = self.parse_expression()?;
17822                        partitions.push(vec![(Identifier::new("__expr__".to_string()), expr)]);
17823                    }
17824                    // Check for ", PARTITION" for multiple partitions
17825                    if self.match_token(TokenType::Comma) {
17826                        if !self.match_token(TokenType::Partition) {
17827                            break;
17828                        }
17829                    } else {
17830                        break;
17831                    }
17832                }
17833                Ok(AlterTableAction::DropPartition {
17834                    partitions,
17835                    if_exists,
17836                })
17837            } else if self.match_token(TokenType::Column) {
17838                // DROP [IF EXISTS] COLUMN [IF EXISTS] name [CASCADE]
17839                // Check for IF EXISTS after COLUMN as well
17840                let if_exists =
17841                    if_exists || self.match_keywords(&[TokenType::If, TokenType::Exists]);
17842                let mut name = self.expect_identifier_with_quoted()?;
17843                // ClickHouse: nested column names like n.ui8
17844                if matches!(
17845                    self.config.dialect,
17846                    Some(crate::dialects::DialectType::ClickHouse)
17847                ) && self.match_token(TokenType::Dot)
17848                {
17849                    let sub = self.expect_identifier_with_quoted()?;
17850                    name.name = format!("{}.{}", name.name, sub.name);
17851                }
17852                let cascade = self.match_token(TokenType::Cascade);
17853                Ok(AlterTableAction::DropColumn {
17854                    name,
17855                    if_exists,
17856                    cascade,
17857                })
17858            } else if self.match_token(TokenType::Constraint) {
17859                // DROP [IF EXISTS] CONSTRAINT name
17860                let name = self.expect_identifier_with_quoted()?;
17861                Ok(AlterTableAction::DropConstraint { name, if_exists })
17862            } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
17863                // DROP FOREIGN KEY name (Oracle/MySQL)
17864                let name = self.expect_identifier_with_quoted()?;
17865                Ok(AlterTableAction::DropForeignKey { name })
17866            } else if self.check_identifier("COLUMNS") && self.check_next(TokenType::LParen) {
17867                // DROP COLUMNS (col1, col2, ...) - Spark/Databricks syntax
17868                self.skip(); // consume COLUMNS
17869                self.expect(TokenType::LParen)?;
17870                let mut names = Vec::new();
17871                loop {
17872                    let name = self.expect_identifier_with_quoted()?;
17873                    names.push(name);
17874                    if !self.match_token(TokenType::Comma) {
17875                        break;
17876                    }
17877                }
17878                self.expect(TokenType::RParen)?;
17879                Ok(AlterTableAction::DropColumns { names })
17880            } else {
17881                // DROP [IF EXISTS] name (implicit column) [CASCADE]
17882                let mut name = self.expect_identifier_with_quoted()?;
17883                // ClickHouse: nested column names like n.ui8
17884                if matches!(
17885                    self.config.dialect,
17886                    Some(crate::dialects::DialectType::ClickHouse)
17887                ) && self.match_token(TokenType::Dot)
17888                {
17889                    let sub = self.expect_identifier_with_quoted()?;
17890                    name.name = format!("{}.{}", name.name, sub.name);
17891                }
17892                let cascade = self.match_token(TokenType::Cascade);
17893                Ok(AlterTableAction::DropColumn {
17894                    name,
17895                    if_exists,
17896                    cascade,
17897                })
17898            }
17899        } else if self.match_token(TokenType::Rename) {
17900            if self.match_token(TokenType::Column) {
17901                // RENAME COLUMN [IF EXISTS] old TO new
17902                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17903                let mut old_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
17904                // ClickHouse: nested column names like n.x
17905                if matches!(
17906                    self.config.dialect,
17907                    Some(crate::dialects::DialectType::ClickHouse)
17908                ) && self.match_token(TokenType::Dot)
17909                {
17910                    let field = self.expect_identifier_with_quoted()?;
17911                    old_name = Identifier {
17912                        name: format!("{}.{}", old_name.name, field.name),
17913                        quoted: false,
17914                        trailing_comments: Vec::new(),
17915                        span: None,
17916                    };
17917                }
17918                self.expect(TokenType::To)?;
17919                let mut new_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
17920                // ClickHouse: nested column names like n.y
17921                if matches!(
17922                    self.config.dialect,
17923                    Some(crate::dialects::DialectType::ClickHouse)
17924                ) && self.match_token(TokenType::Dot)
17925                {
17926                    let field = self.expect_identifier_or_safe_keyword_with_quoted()?;
17927                    new_name = Identifier {
17928                        name: format!("{}.{}", new_name.name, field.name),
17929                        quoted: false,
17930                        trailing_comments: Vec::new(),
17931                        span: None,
17932                    };
17933                }
17934                Ok(AlterTableAction::RenameColumn {
17935                    old_name,
17936                    new_name,
17937                    if_exists,
17938                })
17939            } else if self.match_token(TokenType::To) {
17940                // RENAME TO new_table
17941                let new_name = self.parse_table_ref()?;
17942                Ok(AlterTableAction::RenameTable(new_name))
17943            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
17944                // StarRocks/Doris: RENAME new_name (without TO)
17945                // SQLite: RENAME old_name TO new_name (without COLUMN keyword)
17946                let first_name = self.expect_identifier_with_quoted()?;
17947                if self.match_token(TokenType::To) {
17948                    let new_name = self.expect_identifier_with_quoted()?;
17949                    Ok(AlterTableAction::RenameColumn {
17950                        old_name: first_name,
17951                        new_name,
17952                        if_exists: false,
17953                    })
17954                } else {
17955                    // No TO keyword: treat as RENAME TABLE (StarRocks/Doris)
17956                    Ok(AlterTableAction::RenameTable(TableRef::new(
17957                        first_name.name,
17958                    )))
17959                }
17960            } else {
17961                Err(self.parse_error("Expected COLUMN or TO after RENAME"))
17962            }
17963        } else if self.match_token(TokenType::Alter) {
17964            // Check for ALTER INDEX (MySQL: ALTER TABLE t ALTER INDEX i VISIBLE/INVISIBLE)
17965            if self.match_token(TokenType::Index) {
17966                let name = self.expect_identifier_with_quoted()?;
17967                let visible = if self.match_identifier("VISIBLE") {
17968                    true
17969                } else if self.match_identifier("INVISIBLE") {
17970                    false
17971                } else {
17972                    return Err(
17973                        self.parse_error("Expected VISIBLE or INVISIBLE after ALTER INDEX name")
17974                    );
17975                };
17976                Ok(AlterTableAction::AlterIndex { name, visible })
17977            } else if self.check_identifier("SORTKEY") {
17978                // Redshift: ALTER TABLE t ALTER SORTKEY AUTO|NONE|(col1, col2)
17979                self.skip(); // consume SORTKEY
17980                if self.match_texts(&["AUTO", "NONE"]) {
17981                    let style = self.previous().text.to_ascii_uppercase();
17982                    Ok(AlterTableAction::AlterSortKey {
17983                        this: Some(style),
17984                        expressions: Vec::new(),
17985                        compound: false,
17986                    })
17987                } else if self.check(TokenType::LParen) {
17988                    // (col1, col2) syntax
17989                    let wrapped = self.parse_wrapped_id_vars()?;
17990                    let expressions = if let Some(Expression::Tuple(t)) = wrapped {
17991                        t.expressions
17992                    } else {
17993                        Vec::new()
17994                    };
17995                    Ok(AlterTableAction::AlterSortKey {
17996                        this: None,
17997                        expressions,
17998                        compound: false,
17999                    })
18000                } else {
18001                    Err(self.parse_error("Expected AUTO, NONE, or (columns) after SORTKEY"))
18002                }
18003            } else if self.check_identifier("COMPOUND") {
18004                // Redshift: ALTER TABLE t ALTER COMPOUND SORTKEY (col1, col2)
18005                self.skip(); // consume COMPOUND
18006                if !self.match_identifier("SORTKEY") {
18007                    return Err(self.parse_error("Expected SORTKEY after COMPOUND"));
18008                }
18009                if self.check(TokenType::LParen) {
18010                    let wrapped = self.parse_wrapped_id_vars()?;
18011                    let expressions = if let Some(Expression::Tuple(t)) = wrapped {
18012                        t.expressions
18013                    } else {
18014                        Vec::new()
18015                    };
18016                    Ok(AlterTableAction::AlterSortKey {
18017                        this: None,
18018                        expressions,
18019                        compound: true,
18020                    })
18021                } else {
18022                    Err(self.parse_error("Expected (columns) after COMPOUND SORTKEY"))
18023                }
18024            } else if self.check_identifier("DISTSTYLE") {
18025                // Redshift: ALTER TABLE t ALTER DISTSTYLE ALL|EVEN|AUTO|KEY [DISTKEY col]
18026                self.skip(); // consume DISTSTYLE
18027                if self.match_texts(&["ALL", "EVEN", "AUTO"]) {
18028                    let style = self.previous().text.to_ascii_uppercase();
18029                    Ok(AlterTableAction::AlterDistStyle {
18030                        style,
18031                        distkey: None,
18032                    })
18033                } else if self.match_token(TokenType::Key) || self.match_identifier("KEY") {
18034                    // DISTSTYLE KEY DISTKEY col
18035                    if !self.match_identifier("DISTKEY") {
18036                        return Err(self.parse_error("Expected DISTKEY after DISTSTYLE KEY"));
18037                    }
18038                    let col = self.expect_identifier_with_quoted()?;
18039                    Ok(AlterTableAction::AlterDistStyle {
18040                        style: "KEY".to_string(),
18041                        distkey: Some(col),
18042                    })
18043                } else {
18044                    Err(self.parse_error("Expected ALL, EVEN, AUTO, or KEY after DISTSTYLE"))
18045                }
18046            } else if self.check_identifier("DISTKEY") {
18047                // Redshift: ALTER TABLE t ALTER DISTKEY col (shorthand for DISTSTYLE KEY DISTKEY col)
18048                self.skip(); // consume DISTKEY
18049                let col = self.expect_identifier_with_quoted()?;
18050                Ok(AlterTableAction::AlterDistStyle {
18051                    style: "KEY".to_string(),
18052                    distkey: Some(col),
18053                })
18054            } else {
18055                // ALTER COLUMN
18056                self.match_token(TokenType::Column); // optional COLUMN keyword
18057                let name = self.expect_identifier_with_quoted()?;
18058                let action = self.parse_alter_column_action()?;
18059                Ok(AlterTableAction::AlterColumn {
18060                    name,
18061                    action,
18062                    use_modify_keyword: false,
18063                })
18064            }
18065        } else if self.match_identifier("MODIFY") {
18066            // ClickHouse: MODIFY ORDER BY, MODIFY SETTING, MODIFY TTL, MODIFY QUERY,
18067            // MODIFY COLUMN name type [DEFAULT|MATERIALIZED|ALIAS] [CODEC] [TTL] [COMMENT], etc.
18068            // These are ClickHouse-specific and have richer syntax than MySQL MODIFY COLUMN.
18069            // Consume all ClickHouse MODIFY actions as Raw.
18070            if matches!(
18071                self.config.dialect,
18072                Some(crate::dialects::DialectType::ClickHouse)
18073            ) {
18074                // MODIFY SETTING uses commas between settings (not action separators)
18075                let is_setting =
18076                    self.check(TokenType::Settings) || self.check_identifier("SETTING");
18077                let mut tokens: Vec<(String, TokenType)> =
18078                    vec![("MODIFY".to_string(), TokenType::Var)];
18079                let mut paren_depth = 0i32;
18080                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18081                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_setting {
18082                        break;
18083                    }
18084                    let token = self.advance();
18085                    if token.token_type == TokenType::LParen {
18086                        paren_depth += 1;
18087                    }
18088                    if token.token_type == TokenType::RParen {
18089                        paren_depth -= 1;
18090                    }
18091                    let text = if token.token_type == TokenType::QuotedIdentifier {
18092                        format!("\"{}\"", token.text)
18093                    } else if token.token_type == TokenType::String {
18094                        format!("'{}'", token.text)
18095                    } else {
18096                        token.text.clone()
18097                    };
18098                    tokens.push((text, token.token_type));
18099                }
18100                return Ok(AlterTableAction::Raw {
18101                    sql: self.join_command_tokens(tokens),
18102                });
18103            }
18104            // MODIFY COLUMN (MySQL syntax for altering column type)
18105            self.match_token(TokenType::Column); // optional COLUMN keyword
18106            let name = Identifier::new(self.expect_identifier()?);
18107            // Parse the data type directly (MySQL MODIFY COLUMN col TYPE)
18108            let data_type = self.parse_data_type()?;
18109            // Parse optional COLLATE clause
18110            let collate = if self.match_token(TokenType::Collate) {
18111                if self.check(TokenType::String) {
18112                    Some(self.advance().text)
18113                } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
18114                    Some(self.advance().text)
18115                } else {
18116                    None
18117                }
18118            } else {
18119                None
18120            };
18121            Ok(AlterTableAction::AlterColumn {
18122                name,
18123                action: AlterColumnAction::SetDataType {
18124                    data_type,
18125                    using: None,
18126                    collate,
18127                },
18128                use_modify_keyword: true,
18129            })
18130        } else if self.match_identifier("CHANGE") {
18131            // CHANGE [COLUMN] old_name new_name [data_type] [COMMENT 'comment'] - Hive/MySQL/SingleStore syntax
18132            // In SingleStore, data_type can be omitted for simple renames
18133            self.match_token(TokenType::Column); // optional COLUMN keyword
18134            let old_name = Identifier::new(self.expect_identifier()?);
18135            let new_name = Identifier::new(self.expect_identifier()?);
18136            // Try to parse data type - it's optional in SingleStore
18137            let data_type = if !self.is_at_end()
18138                && !self.check(TokenType::Comment)
18139                && !self.check(TokenType::Comma)
18140                && !self.check(TokenType::Semicolon)
18141            {
18142                // Check if next token could start a data type
18143                let tok = self.peek();
18144                if tok.token_type.is_keyword()
18145                    || tok.token_type == TokenType::Identifier
18146                    || tok.token_type == TokenType::Var
18147                {
18148                    Some(self.parse_data_type()?)
18149                } else {
18150                    None
18151                }
18152            } else {
18153                None
18154            };
18155            let comment = if self.match_token(TokenType::Comment) {
18156                Some(self.expect_string()?)
18157            } else {
18158                None
18159            };
18160            let cascade = self.match_text_seq(&["CASCADE"]);
18161            // Also check for RESTRICT (the opposite, just consume it)
18162            if !cascade {
18163                self.match_text_seq(&["RESTRICT"]);
18164            }
18165            Ok(AlterTableAction::ChangeColumn {
18166                old_name,
18167                new_name,
18168                data_type,
18169                comment,
18170                cascade,
18171            })
18172        } else if self.match_token(TokenType::Constraint) {
18173            // CONSTRAINT name ... (implicit ADD, CONSTRAINT already consumed)
18174            // Parse the constraint name and then the constraint definition
18175            let name = Some(self.expect_identifier_with_quoted()?);
18176            let constraint = self.parse_constraint_definition(name)?;
18177            Ok(AlterTableAction::AddConstraint(constraint))
18178        } else if self.check(TokenType::PrimaryKey)
18179            || self.check(TokenType::ForeignKey)
18180            || self.check(TokenType::Unique)
18181        {
18182            // ADD CONSTRAINT (implicit ADD, no CONSTRAINT keyword)
18183            let constraint = self.parse_table_constraint()?;
18184            Ok(AlterTableAction::AddConstraint(constraint))
18185        } else if self.match_token(TokenType::Delete) {
18186            // ALTER TABLE t DELETE WHERE x = 1 (BigQuery syntax)
18187            self.expect(TokenType::Where)?;
18188            let where_clause = self.parse_expression()?;
18189            Ok(AlterTableAction::Delete { where_clause })
18190        } else if self.match_keyword("SWAP") {
18191            // Snowflake: ALTER TABLE a SWAP WITH b
18192            self.expect(TokenType::With)?;
18193            let target = self.parse_table_ref()?;
18194            Ok(AlterTableAction::SwapWith(target))
18195        } else if self.match_token(TokenType::Set) {
18196            // TSQL: ALTER TABLE t SET (SYSTEM_VERSIONING=ON, DATA_DELETION=ON, ...)
18197            if self.check(TokenType::LParen) {
18198                self.skip(); // consume (
18199                let mut expressions = Vec::new();
18200                loop {
18201                    if self.check(TokenType::RParen) {
18202                        break;
18203                    }
18204                    if self.check_identifier("SYSTEM_VERSIONING") {
18205                        let expr = self.parse_system_versioning_option()?;
18206                        expressions.push(expr);
18207                    } else if self.check_identifier("DATA_DELETION") {
18208                        let expr = self.parse_data_deletion_option()?;
18209                        expressions.push(expr);
18210                    } else {
18211                        // Generic key=value (e.g., FILESTREAM_ON = 'test')
18212                        let expr = self.parse_expression()?;
18213                        expressions.push(expr);
18214                    }
18215                    if !self.match_token(TokenType::Comma) {
18216                        break;
18217                    }
18218                }
18219                self.expect(TokenType::RParen)?;
18220                Ok(AlterTableAction::SetOptions { expressions })
18221            } else if self.match_keyword("TAG") {
18222                // Snowflake: SET TAG key='value', ... (key can be qualified like schema.tagname)
18223                let mut tags = Vec::new();
18224                loop {
18225                    // Parse qualified tag name (e.g., foo.bar or just bar)
18226                    let mut key = self.expect_identifier_or_keyword()?;
18227                    while self.match_token(TokenType::Dot) {
18228                        let next = self.expect_identifier_or_keyword()?;
18229                        key = format!("{}.{}", key, next);
18230                    }
18231                    self.expect(TokenType::Eq)?;
18232                    let value = self.parse_primary()?;
18233                    tags.push((key, value));
18234                    if !self.match_token(TokenType::Comma) {
18235                        break;
18236                    }
18237                }
18238                Ok(AlterTableAction::SetTag { expressions: tags })
18239            } else if self.check_identifier("LOGGED") {
18240                // PostgreSQL: ALTER TABLE t SET LOGGED
18241                self.skip();
18242                Ok(AlterTableAction::SetAttribute {
18243                    attribute: "LOGGED".to_string(),
18244                })
18245            } else if self.check_identifier("UNLOGGED") {
18246                // PostgreSQL: ALTER TABLE t SET UNLOGGED
18247                self.skip();
18248                Ok(AlterTableAction::SetAttribute {
18249                    attribute: "UNLOGGED".to_string(),
18250                })
18251            } else if self.match_identifier("WITHOUT") {
18252                // PostgreSQL: ALTER TABLE t SET WITHOUT CLUSTER/OIDS
18253                let what = self.expect_identifier_or_keyword()?;
18254                Ok(AlterTableAction::SetAttribute {
18255                    attribute: format!("WITHOUT {}", what),
18256                })
18257            } else if self.check_identifier("ACCESS") {
18258                // PostgreSQL: ALTER TABLE t SET ACCESS METHOD method
18259                self.skip();
18260                // Consume "METHOD"
18261                if !self.match_identifier("METHOD") {
18262                    return Err(self.parse_error("Expected METHOD after ACCESS"));
18263                }
18264                let method = self.expect_identifier_or_keyword()?;
18265                Ok(AlterTableAction::SetAttribute {
18266                    attribute: format!("ACCESS METHOD {}", method),
18267                })
18268            } else if self.check_identifier("TABLESPACE") {
18269                // PostgreSQL: ALTER TABLE t SET TABLESPACE tablespace
18270                self.skip();
18271                let name = self.expect_identifier_or_keyword()?;
18272                Ok(AlterTableAction::SetAttribute {
18273                    attribute: format!("TABLESPACE {}", name),
18274                })
18275            } else if self.check_identifier("STAGE_FILE_FORMAT") {
18276                // Snowflake: ALTER TABLE t SET STAGE_FILE_FORMAT = (options)
18277                self.skip();
18278                let options = self.parse_wrapped_options()?;
18279                Ok(AlterTableAction::SetStageFileFormat { options })
18280            } else if self.check_identifier("STAGE_COPY_OPTIONS") {
18281                // Snowflake: ALTER TABLE t SET STAGE_COPY_OPTIONS = (options)
18282                self.skip();
18283                let options = self.parse_wrapped_options()?;
18284                Ok(AlterTableAction::SetStageCopyOptions { options })
18285            } else if self.match_token(TokenType::Authorization) {
18286                // Trino: ALTER TABLE t SET AUTHORIZATION [ROLE] user
18287                let mut auth_text = String::new();
18288                if self.match_texts(&["ROLE"]) {
18289                    auth_text.push_str("ROLE ");
18290                }
18291                let user = self.expect_identifier_or_keyword()?;
18292                auth_text.push_str(&user);
18293                Ok(AlterTableAction::SetAttribute {
18294                    attribute: format!("AUTHORIZATION {}", auth_text),
18295                })
18296            } else if self.match_identifier("PROPERTIES") {
18297                // Trino: ALTER TABLE t SET PROPERTIES x = 'y', ...
18298                let mut properties = Vec::new();
18299                loop {
18300                    // Parse property name (could be identifier or string literal)
18301                    let key = if self.check(TokenType::String) {
18302                        self.expect_string()?
18303                    } else {
18304                        self.expect_identifier_or_keyword()?
18305                    };
18306                    self.expect(TokenType::Eq)?;
18307                    // Parse value (could be DEFAULT or an expression)
18308                    let value = if self.match_token(TokenType::Default) {
18309                        // Use Var instead of Identifier so it won't be quoted
18310                        Expression::Var(Box::new(crate::expressions::Var {
18311                            this: "DEFAULT".to_string(),
18312                        }))
18313                    } else {
18314                        self.parse_expression()?
18315                    };
18316                    properties.push((key, value));
18317                    if !self.match_token(TokenType::Comma) {
18318                        break;
18319                    }
18320                }
18321                Ok(AlterTableAction::SetProperty { properties })
18322            } else if self.match_text_seq(&["TABLE", "PROPERTIES"]) {
18323                // Redshift: ALTER TABLE t SET TABLE PROPERTIES ('a' = '5', 'b' = 'c')
18324                self.expect(TokenType::LParen)?;
18325                let mut properties = Vec::new();
18326                loop {
18327                    if self.check(TokenType::RParen) {
18328                        break;
18329                    }
18330                    // Parse key (string literal)
18331                    let key = self.parse_primary()?;
18332                    self.expect(TokenType::Eq)?;
18333                    // Parse value (string literal)
18334                    let value = self.parse_primary()?;
18335                    properties.push((key, value));
18336                    if !self.match_token(TokenType::Comma) {
18337                        break;
18338                    }
18339                }
18340                self.expect(TokenType::RParen)?;
18341                Ok(AlterTableAction::SetTableProperties { properties })
18342            } else if self.match_text_seq(&["LOCATION"]) {
18343                // Redshift: ALTER TABLE t SET LOCATION 's3://bucket/folder/'
18344                let location = self.expect_string()?;
18345                Ok(AlterTableAction::SetLocation { location })
18346            } else if self.match_text_seq(&["FILE", "FORMAT"]) {
18347                // Redshift: ALTER TABLE t SET FILE FORMAT AVRO
18348                let format = self.expect_identifier_or_keyword()?;
18349                Ok(AlterTableAction::SetFileFormat { format })
18350            } else {
18351                // Snowflake: SET property=value, ...
18352                let mut properties = Vec::new();
18353                loop {
18354                    let key = self.expect_identifier_or_keyword()?;
18355                    self.expect(TokenType::Eq)?;
18356                    let value = self.parse_expression()?;
18357                    properties.push((key, value));
18358                    if !self.match_token(TokenType::Comma) {
18359                        break;
18360                    }
18361                }
18362                Ok(AlterTableAction::SetProperty { properties })
18363            }
18364        } else if self.match_keyword("UNSET") {
18365            // Snowflake: ALTER TABLE t UNSET property or UNSET TAG key
18366            if self.match_keyword("TAG") {
18367                // UNSET TAG key1, key2 (keys can be qualified like schema.tagname)
18368                let mut names = Vec::new();
18369                loop {
18370                    let mut name = self.expect_identifier_or_keyword()?;
18371                    while self.match_token(TokenType::Dot) {
18372                        let next = self.expect_identifier_or_keyword()?;
18373                        name = format!("{}.{}", name, next);
18374                    }
18375                    names.push(name);
18376                    if !self.match_token(TokenType::Comma) {
18377                        break;
18378                    }
18379                }
18380                Ok(AlterTableAction::UnsetTag { names })
18381            } else {
18382                // UNSET property1, property2
18383                let mut properties = Vec::new();
18384                loop {
18385                    let name = self.expect_identifier_or_keyword()?;
18386                    properties.push(name);
18387                    if !self.match_token(TokenType::Comma) {
18388                        break;
18389                    }
18390                }
18391                Ok(AlterTableAction::UnsetProperty { properties })
18392            }
18393        } else if self.match_keyword("CLUSTER") {
18394            // Snowflake: ALTER TABLE t CLUSTER BY (col1, col2 DESC)
18395            self.expect(TokenType::By)?;
18396            self.expect(TokenType::LParen)?;
18397            // Parse ordered expressions (can have ASC/DESC modifiers)
18398            let ordered = self.parse_order_by_list()?;
18399            // Convert Ordered to Expression (wrapping in Ordered if it has ordering)
18400            let expressions: Vec<Expression> = ordered
18401                .into_iter()
18402                .map(|o| Expression::Ordered(Box::new(o)))
18403                .collect();
18404            self.expect(TokenType::RParen)?;
18405            Ok(AlterTableAction::ClusterBy { expressions })
18406        } else if self.match_token(TokenType::Replace) {
18407            // ClickHouse: REPLACE PARTITION expr FROM table
18408            if self.match_token(TokenType::Partition) {
18409                let partition_expr = if self.match_text_seq(&["ALL"]) {
18410                    Expression::Identifier(Identifier::new("ALL".to_string()))
18411                } else if self.match_text_seq(&["ID"]) {
18412                    let id_val = self.parse_expression()?;
18413                    // Store as Raw to preserve "ID <value>" format
18414                    let id_str = match &id_val {
18415                        Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
18416                            let Literal::String(s) = lit.as_ref() else {
18417                                unreachable!()
18418                            };
18419                            format!("ID '{}'", s)
18420                        }
18421                        _ => format!("ID {}", "?"),
18422                    };
18423                    Expression::Raw(Raw { sql: id_str })
18424                } else {
18425                    self.parse_expression()?
18426                };
18427                let source = if self.match_token(TokenType::From) {
18428                    let tref = self.parse_table_ref()?;
18429                    Some(Box::new(Expression::Table(Box::new(tref))))
18430                } else {
18431                    None
18432                };
18433                Ok(AlterTableAction::ReplacePartition {
18434                    partition: partition_expr,
18435                    source,
18436                })
18437            } else {
18438                Err(self.parse_error("Expected PARTITION after REPLACE in ALTER TABLE"))
18439            }
18440        } else if matches!(
18441            self.config.dialect,
18442            Some(crate::dialects::DialectType::ClickHouse)
18443        ) {
18444            // ClickHouse-specific ALTER TABLE mutations: UPDATE, DELETE, DETACH, ATTACH,
18445            // FREEZE, UNFREEZE, MATERIALIZE, CLEAR, COMMENT COLUMN, MODIFY ORDER BY,
18446            // MOVE PARTITION, FETCH PARTITION, ADD INDEX, DROP INDEX, CLEAR INDEX
18447            // For ClickHouse, consume any unrecognized ALTER TABLE action as Raw
18448            // (covers UPDATE, DELETE, DETACH, ATTACH, FREEZE, MOVE, FETCH, etc.)
18449            {
18450                let keyword = self.advance().text.clone();
18451                let mut tokens: Vec<(String, TokenType)> = vec![(keyword, TokenType::Var)];
18452                let mut paren_depth = 0i32;
18453                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18454                    // Stop at comma only when at top-level (not inside parens) — it separates ALTER actions
18455                    if self.check(TokenType::Comma) && paren_depth == 0 {
18456                        break;
18457                    }
18458                    let token = self.advance();
18459                    if token.token_type == TokenType::LParen {
18460                        paren_depth += 1;
18461                    }
18462                    if token.token_type == TokenType::RParen {
18463                        paren_depth -= 1;
18464                    }
18465                    let text = if token.token_type == TokenType::QuotedIdentifier {
18466                        format!("\"{}\"", token.text)
18467                    } else if token.token_type == TokenType::String {
18468                        format!("'{}'", token.text)
18469                    } else {
18470                        token.text.clone()
18471                    };
18472                    tokens.push((text, token.token_type));
18473                }
18474                Ok(AlterTableAction::Raw {
18475                    sql: self.join_command_tokens(tokens),
18476                })
18477            }
18478        } else if self.check_identifier("REORGANIZE")
18479            || self.check_identifier("COALESCE")
18480            || self.check_identifier("EXCHANGE")
18481            || self.check_identifier("ANALYZE")
18482            || self.check_identifier("OPTIMIZE")
18483            || self.check_identifier("REBUILD")
18484            || self.check_identifier("REPAIR")
18485            || self.check_identifier("DISCARD")
18486            || self.check_identifier("IMPORT")
18487        {
18488            // MySQL partition operations: REORGANIZE PARTITION, COALESCE PARTITION, etc.
18489            // Consume as Raw, respecting parenthesis depth
18490            let keyword = self.advance().text.clone();
18491            let mut tokens: Vec<(String, TokenType)> = vec![(keyword, TokenType::Var)];
18492            let mut paren_depth = 0i32;
18493            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18494                if self.check(TokenType::Comma) && paren_depth == 0 {
18495                    break;
18496                }
18497                let token = self.advance();
18498                if token.token_type == TokenType::LParen {
18499                    paren_depth += 1;
18500                }
18501                if token.token_type == TokenType::RParen {
18502                    paren_depth -= 1;
18503                    if paren_depth < 0 {
18504                        break;
18505                    }
18506                }
18507                let text = if token.token_type == TokenType::QuotedIdentifier {
18508                    format!("\"{}\"", token.text)
18509                } else if token.token_type == TokenType::String {
18510                    format!("'{}'", token.text)
18511                } else {
18512                    token.text.clone()
18513                };
18514                tokens.push((text, token.token_type));
18515            }
18516            Ok(AlterTableAction::Raw {
18517                sql: self.join_command_tokens(tokens),
18518            })
18519        } else {
18520            Err(self.parse_error(format!(
18521                "Expected ADD, DROP, RENAME, ALTER, SET, UNSET, SWAP, CLUSTER, or REPLACE in ALTER TABLE, got {:?}",
18522                self.peek().token_type
18523            )))
18524        }
18525    }
18526
18527    /// Parse TSQL SYSTEM_VERSIONING option in ALTER TABLE SET (...)
18528    /// Handles: SYSTEM_VERSIONING=OFF, SYSTEM_VERSIONING=ON, SYSTEM_VERSIONING=ON(HISTORY_TABLE=..., ...)
18529    fn parse_system_versioning_option(&mut self) -> Result<Expression> {
18530        self.skip(); // consume SYSTEM_VERSIONING
18531        self.expect(TokenType::Eq)?;
18532
18533        let mut prop = WithSystemVersioningProperty {
18534            on: None,
18535            this: None,
18536            data_consistency: None,
18537            retention_period: None,
18538            with_: None,
18539        };
18540
18541        if self.match_identifier("OFF") {
18542            // SYSTEM_VERSIONING=OFF
18543            // on is None => generates OFF
18544            return Ok(Expression::WithSystemVersioningProperty(Box::new(prop)));
18545        }
18546
18547        // SYSTEM_VERSIONING=ON or SYSTEM_VERSIONING=ON(...)
18548        if self.match_token(TokenType::On) || self.match_identifier("ON") {
18549            prop.on = Some(Box::new(Expression::Boolean(BooleanLiteral {
18550                value: true,
18551            })));
18552        }
18553
18554        if self.match_token(TokenType::LParen) {
18555            // Parse options inside ON(...)
18556            loop {
18557                if self.check(TokenType::RParen) {
18558                    break;
18559                }
18560                if self.match_identifier("HISTORY_TABLE") {
18561                    self.expect(TokenType::Eq)?;
18562                    let table = self.parse_table_ref()?;
18563                    prop.this = Some(Box::new(Expression::Table(Box::new(table))));
18564                } else if self.match_identifier("DATA_CONSISTENCY_CHECK") {
18565                    self.expect(TokenType::Eq)?;
18566                    let val = self.expect_identifier_or_keyword()?;
18567                    prop.data_consistency = Some(Box::new(Expression::Identifier(
18568                        Identifier::new(val.to_ascii_uppercase()),
18569                    )));
18570                } else if self.match_identifier("HISTORY_RETENTION_PERIOD") {
18571                    self.expect(TokenType::Eq)?;
18572                    if let Some(rp) = self.parse_retention_period()? {
18573                        prop.retention_period = Some(Box::new(rp));
18574                    }
18575                } else {
18576                    // Skip unknown options
18577                    self.skip();
18578                }
18579                if !self.match_token(TokenType::Comma) {
18580                    break;
18581                }
18582            }
18583            self.expect(TokenType::RParen)?;
18584        }
18585
18586        Ok(Expression::WithSystemVersioningProperty(Box::new(prop)))
18587    }
18588
18589    /// Parse TSQL DATA_DELETION option in ALTER TABLE SET (...)
18590    /// Handles: DATA_DELETION=ON, DATA_DELETION=OFF, DATA_DELETION=ON(FILTER_COLUMN=..., RETENTION_PERIOD=...)
18591    fn parse_data_deletion_option(&mut self) -> Result<Expression> {
18592        self.skip(); // consume DATA_DELETION
18593        self.expect(TokenType::Eq)?;
18594
18595        let on = if self.match_identifier("ON") || self.match_token(TokenType::On) {
18596            true
18597        } else if self.match_identifier("OFF") {
18598            false
18599        } else {
18600            false
18601        };
18602
18603        let on_expr = Box::new(Expression::Boolean(BooleanLiteral { value: on }));
18604        let mut filter_column = None;
18605        let mut retention_period = None;
18606
18607        if self.match_token(TokenType::LParen) {
18608            loop {
18609                if self.check(TokenType::RParen) {
18610                    break;
18611                }
18612                if self.match_identifier("FILTER_COLUMN") {
18613                    self.expect(TokenType::Eq)?;
18614                    let col = self.expect_identifier_or_keyword()?;
18615                    filter_column = Some(Box::new(Expression::boxed_column(Column {
18616                        name: Identifier::new(col),
18617                        table: None,
18618                        join_mark: false,
18619                        trailing_comments: Vec::new(),
18620                        span: None,
18621                        inferred_type: None,
18622                    })));
18623                } else if self.match_identifier("RETENTION_PERIOD") {
18624                    self.expect(TokenType::Eq)?;
18625                    if let Some(rp) = self.parse_retention_period()? {
18626                        retention_period = Some(Box::new(rp));
18627                    }
18628                } else {
18629                    self.skip();
18630                }
18631                if !self.match_token(TokenType::Comma) {
18632                    break;
18633                }
18634            }
18635            self.expect(TokenType::RParen)?;
18636        }
18637
18638        Ok(Expression::DataDeletionProperty(Box::new(
18639            DataDeletionProperty {
18640                on: on_expr,
18641                filter_column,
18642                retention_period,
18643            },
18644        )))
18645    }
18646
18647    /// Parse ALTER COLUMN action
18648    fn parse_alter_column_action(&mut self) -> Result<AlterColumnAction> {
18649        if self.match_token(TokenType::Set) {
18650            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
18651                Ok(AlterColumnAction::SetNotNull)
18652            } else if self.match_token(TokenType::Default) {
18653                let expr = self.parse_primary()?;
18654                Ok(AlterColumnAction::SetDefault(expr))
18655            } else if self.match_identifier("DATA") {
18656                // SET DATA TYPE
18657                // TYPE can be a keyword token or identifier
18658                let _ = self.match_token(TokenType::Type) || self.match_identifier("TYPE");
18659                let data_type = self.parse_data_type()?;
18660                // Optional COLLATE
18661                let collate = if self.match_token(TokenType::Collate) {
18662                    Some(self.expect_identifier_or_keyword()?)
18663                } else {
18664                    None
18665                };
18666                // Optional USING expression
18667                let using = if self.match_token(TokenType::Using) {
18668                    Some(self.parse_expression()?)
18669                } else {
18670                    None
18671                };
18672                Ok(AlterColumnAction::SetDataType {
18673                    data_type,
18674                    using,
18675                    collate,
18676                })
18677            } else if self.match_identifier("VISIBLE") {
18678                Ok(AlterColumnAction::SetVisible)
18679            } else if self.match_identifier("INVISIBLE") {
18680                Ok(AlterColumnAction::SetInvisible)
18681            } else {
18682                Err(self.parse_error("Expected NOT NULL, DEFAULT, VISIBLE, or INVISIBLE after SET"))
18683            }
18684        } else if self.match_token(TokenType::Drop) {
18685            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
18686                Ok(AlterColumnAction::DropNotNull)
18687            } else if self.match_token(TokenType::Default) {
18688                Ok(AlterColumnAction::DropDefault)
18689            } else {
18690                Err(self.parse_error("Expected NOT NULL or DEFAULT after DROP"))
18691            }
18692        } else if self.match_token(TokenType::Comment) {
18693            // ALTER COLUMN col COMMENT 'comment'
18694            let comment = self.expect_string()?;
18695            Ok(AlterColumnAction::Comment(comment))
18696        } else if self.match_token(TokenType::Type)
18697            || self.match_identifier("TYPE")
18698            || self.is_identifier_token()
18699        {
18700            // TYPE data_type or just data_type (PostgreSQL/Redshift: ALTER COLUMN col TYPE datatype)
18701            let data_type = self.parse_data_type()?;
18702            // Optional COLLATE
18703            let collate = if self.match_token(TokenType::Collate) {
18704                Some(self.expect_identifier_or_keyword()?)
18705            } else {
18706                None
18707            };
18708            // Optional USING expression
18709            let using = if self.match_token(TokenType::Using) {
18710                Some(self.parse_expression()?)
18711            } else {
18712                None
18713            };
18714            Ok(AlterColumnAction::SetDataType {
18715                data_type,
18716                using,
18717                collate,
18718            })
18719        } else {
18720            Err(self.parse_error("Expected SET, DROP, or TYPE in ALTER COLUMN"))
18721        }
18722    }
18723
18724    /// Parse TRUNCATE statement
18725    fn parse_truncate(&mut self) -> Result<Expression> {
18726        self.expect(TokenType::Truncate)?;
18727
18728        // ClickHouse: TRUNCATE ALL TABLES FROM [IF EXISTS] db
18729        if matches!(
18730            self.config.dialect,
18731            Some(crate::dialects::DialectType::ClickHouse)
18732        ) && self.check_identifier("ALL")
18733            && self.current + 1 < self.tokens.len()
18734            && self.tokens[self.current + 1]
18735                .text
18736                .eq_ignore_ascii_case("TABLES")
18737        {
18738            // Consume remaining tokens as Command
18739            let mut parts = vec!["TRUNCATE".to_string()];
18740            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18741                let token = self.advance();
18742                if token.token_type == TokenType::String {
18743                    parts.push(format!("'{}'", token.text));
18744                } else {
18745                    parts.push(token.text.clone());
18746                }
18747            }
18748            return Ok(Expression::Command(Box::new(crate::expressions::Command {
18749                this: parts.join(" "),
18750            })));
18751        }
18752
18753        let target = if self.match_token(TokenType::Database) {
18754            TruncateTarget::Database
18755        } else {
18756            // ClickHouse: TRUNCATE TEMPORARY TABLE t
18757            self.match_token(TokenType::Temporary);
18758            self.match_token(TokenType::Table); // optional TABLE keyword
18759            TruncateTarget::Table
18760        };
18761
18762        // Parse optional IF EXISTS
18763        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
18764
18765        // Parse first table with optional ONLY modifier
18766        let has_only = self.match_token(TokenType::Only);
18767        let mut table = self.parse_table_ref()?;
18768        if has_only {
18769            table.only = true;
18770        }
18771
18772        // Check for * suffix on table name (PostgreSQL: inherit children)
18773        let first_star = self.match_token(TokenType::Star);
18774
18775        // TSQL: WITH (PARTITIONS(1, 2 TO 5, 10 TO 20, 84))
18776        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
18777            if let Some(hint_expr) = self.parse_truncate_table_hints()? {
18778                match hint_expr {
18779                    Expression::Tuple(tuple) => {
18780                        table.hints = tuple.expressions;
18781                    }
18782                    other => {
18783                        table.hints = vec![other];
18784                    }
18785                }
18786            }
18787        }
18788
18789        // ClickHouse: ON CLUSTER clause
18790        let on_cluster = self.parse_on_cluster_clause()?;
18791
18792        // Parse additional tables for multi-table TRUNCATE
18793        let mut extra_tables = Vec::new();
18794        if first_star {
18795            // The first table has a * suffix, so create an entry for it
18796            extra_tables.push(TruncateTableEntry {
18797                table: table.clone(),
18798                star: true,
18799            });
18800        }
18801        while self.match_token(TokenType::Comma) {
18802            let extra_only = self.match_token(TokenType::Only);
18803            let mut extra_table = self.parse_table_ref()?;
18804            if extra_only {
18805                extra_table.only = true;
18806            }
18807            let extra_star = self.match_token(TokenType::Star);
18808            extra_tables.push(TruncateTableEntry {
18809                table: extra_table,
18810                star: extra_star,
18811            });
18812        }
18813
18814        // Parse RESTART IDENTITY / CONTINUE IDENTITY
18815        // RESTART is TokenType::Restart keyword, IDENTITY is TokenType::Identity keyword
18816        let identity = if self.match_token(TokenType::Restart) {
18817            self.match_token(TokenType::Identity);
18818            Some(TruncateIdentity::Restart)
18819        } else if self.match_identifier("CONTINUE") {
18820            self.match_token(TokenType::Identity);
18821            Some(TruncateIdentity::Continue)
18822        } else {
18823            None
18824        };
18825
18826        // Parse CASCADE or RESTRICT
18827        // CASCADE is TokenType::Cascade keyword, RESTRICT is TokenType::Restrict keyword
18828        let cascade = self.match_token(TokenType::Cascade);
18829        let restrict = if !cascade {
18830            self.match_token(TokenType::Restrict)
18831        } else {
18832            false
18833        };
18834
18835        // Parse Hive PARTITION clause: PARTITION(key = value, ...)
18836        // parse_partition consumes the PARTITION keyword itself
18837        let partition = self.parse_partition()?;
18838
18839        // ClickHouse: TRUNCATE TABLE t SETTINGS key=value, ...
18840        if matches!(
18841            self.config.dialect,
18842            Some(crate::dialects::DialectType::ClickHouse)
18843        ) && self.match_token(TokenType::Settings)
18844        {
18845            // Consume settings expressions (they're not stored in the AST for TRUNCATE)
18846            loop {
18847                let _ = self.parse_expression()?;
18848                if !self.match_token(TokenType::Comma) {
18849                    break;
18850                }
18851            }
18852        }
18853
18854        Ok(Expression::Truncate(Box::new(Truncate {
18855            target,
18856            if_exists,
18857            table,
18858            on_cluster,
18859            cascade,
18860            extra_tables,
18861            identity,
18862            restrict,
18863            partition: partition.map(Box::new),
18864        })))
18865    }
18866
18867    /// Parse VALUES table constructor: VALUES (1, 'a'), (2, 'b')
18868    fn parse_values(&mut self) -> Result<Expression> {
18869        self.expect(TokenType::Values)?;
18870
18871        let mut expressions = Vec::new();
18872
18873        // Handle bare VALUES without parentheses: VALUES 1, 2, 3 -> VALUES (1), (2), (3)
18874        if !self.check(TokenType::LParen) {
18875            loop {
18876                let val = self.parse_expression()?;
18877                expressions.push(Tuple {
18878                    expressions: vec![val],
18879                });
18880                if !self.match_token(TokenType::Comma) {
18881                    break;
18882                }
18883            }
18884        } else {
18885            loop {
18886                self.expect(TokenType::LParen)?;
18887                // Parse VALUES tuple elements with optional AS aliases (Hive syntax)
18888                let row_values = self.parse_values_expression_list()?;
18889                self.expect(TokenType::RParen)?;
18890
18891                expressions.push(Tuple {
18892                    expressions: row_values,
18893                });
18894
18895                if !self.match_token(TokenType::Comma) {
18896                    break;
18897                }
18898                // ClickHouse: allow trailing comma after last tuple
18899                if matches!(
18900                    self.config.dialect,
18901                    Some(crate::dialects::DialectType::ClickHouse)
18902                ) && !self.check(TokenType::LParen)
18903                {
18904                    break;
18905                }
18906            }
18907        }
18908
18909        // Check for alias: VALUES (1, 2) AS new_data or VALUES (1, 2) new_data
18910        let (alias, column_aliases) = if self.match_token(TokenType::As) {
18911            let alias_name = self.expect_identifier()?;
18912            let alias = Some(Identifier::new(alias_name));
18913
18914            // Check for column aliases: AS new_data(a, b)
18915            let col_aliases = if self.match_token(TokenType::LParen) {
18916                let aliases = self.parse_identifier_list()?;
18917                self.expect(TokenType::RParen)?;
18918                aliases
18919            } else {
18920                Vec::new()
18921            };
18922            (alias, col_aliases)
18923        } else if self.check(TokenType::Var) && !self.check_keyword() {
18924            // Implicit alias: VALUES (0) foo(bar)
18925            let alias_name = self.advance().text.clone();
18926            let alias = Some(Identifier::new(alias_name));
18927            let col_aliases = if self.match_token(TokenType::LParen) {
18928                let aliases = self.parse_identifier_list()?;
18929                self.expect(TokenType::RParen)?;
18930                aliases
18931            } else {
18932                Vec::new()
18933            };
18934            (alias, col_aliases)
18935        } else {
18936            (None, Vec::new())
18937        };
18938
18939        // VALUES can be followed by set operations (UNION, etc.)
18940        let values_expr = Expression::Values(Box::new(Values {
18941            expressions,
18942            alias,
18943            column_aliases,
18944        }));
18945
18946        // Check for set operations after VALUES
18947        self.parse_set_operation(values_expr)
18948    }
18949
18950    /// Parse USE statement: USE db, USE DATABASE x, USE SCHEMA x.y, USE ROLE x, etc.
18951    fn parse_use(&mut self) -> Result<Expression> {
18952        self.expect(TokenType::Use)?;
18953
18954        // Check for Snowflake: USE SECONDARY ROLES ALL|NONE|role1, role2, ...
18955        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SECONDARY") {
18956            self.skip(); // consume SECONDARY
18957                         // Check for ROLES
18958            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLES") {
18959                self.skip(); // consume ROLES
18960                             // Parse ALL, NONE, or comma-separated role list
18961                let mut roles = Vec::new();
18962                loop {
18963                    if self.check(TokenType::Var)
18964                        || self.check(TokenType::All)
18965                        || self.check(TokenType::Identifier)
18966                    {
18967                        let role = self.advance().text.clone();
18968                        roles.push(role);
18969                        if !self.match_token(TokenType::Comma) {
18970                            break;
18971                        }
18972                    } else {
18973                        break;
18974                    }
18975                }
18976                let name = if roles.is_empty() {
18977                    "ALL".to_string()
18978                } else {
18979                    roles.join(", ")
18980                };
18981                return Ok(Expression::Use(Box::new(Use {
18982                    kind: Some(UseKind::SecondaryRoles),
18983                    this: Identifier::new(name),
18984                })));
18985            }
18986        }
18987
18988        // Check for kind: DATABASE, SCHEMA, ROLE, WAREHOUSE, CATALOG
18989        // Note: ROLE and CATALOG are not keywords, so we check the text
18990        let kind = if self.match_token(TokenType::Database) {
18991            Some(UseKind::Database)
18992        } else if self.match_token(TokenType::Schema) {
18993            Some(UseKind::Schema)
18994        } else if self.match_token(TokenType::Warehouse) {
18995            Some(UseKind::Warehouse)
18996        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLE") {
18997            self.skip();
18998            Some(UseKind::Role)
18999        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("CATALOG") {
19000            self.skip();
19001            Some(UseKind::Catalog)
19002        } else {
19003            None
19004        };
19005
19006        // Parse the name (can be qualified like x.y)
19007        // Use expect_identifier_or_keyword_with_quoted because names like "default", "system" are valid
19008        let mut ident = self.expect_identifier_or_keyword_with_quoted()?;
19009
19010        // Handle qualified names like schema.table for USE SCHEMA x.y
19011        if self.match_token(TokenType::Dot) {
19012            let second_part = self.expect_identifier_or_keyword_with_quoted()?;
19013            ident.name = format!("{}.{}", ident.name, second_part.name);
19014        }
19015
19016        Ok(Expression::Use(Box::new(Use { kind, this: ident })))
19017    }
19018
19019    /// Parse EXPORT DATA statement (BigQuery)
19020    /// EXPORT DATA [WITH CONNECTION connection] OPTIONS (...) AS SELECT ...
19021    fn parse_export_data(&mut self) -> Result<Expression> {
19022        self.skip(); // consume EXPORT
19023
19024        // Expect DATA
19025        if !self.match_identifier("DATA") {
19026            return Err(self.parse_error("Expected DATA after EXPORT"));
19027        }
19028
19029        // Optional: WITH CONNECTION connection
19030        let connection = if self.match_text_seq(&["WITH", "CONNECTION"]) {
19031            // Parse connection identifier (can be qualified: project.location.connection)
19032            let first = self.expect_identifier()?;
19033            let connection_name = if self.match_token(TokenType::Dot) {
19034                let second = self.expect_identifier()?;
19035                if self.match_token(TokenType::Dot) {
19036                    let third = self.expect_identifier()?;
19037                    format!("{}.{}.{}", first, second, third)
19038                } else {
19039                    format!("{}.{}", first, second)
19040                }
19041            } else {
19042                first
19043            };
19044            Some(Box::new(Expression::Identifier(Identifier::new(
19045                connection_name,
19046            ))))
19047        } else {
19048            None
19049        };
19050
19051        // Expect OPTIONS (...)
19052        let options = if self.match_identifier("OPTIONS") {
19053            self.parse_options_list()?
19054        } else {
19055            Vec::new()
19056        };
19057
19058        // Expect AS
19059        self.expect(TokenType::As)?;
19060
19061        // Parse the SELECT query
19062        let query = self.parse_statement()?;
19063
19064        Ok(Expression::Export(Box::new(Export {
19065            this: Box::new(query),
19066            connection,
19067            options,
19068        })))
19069    }
19070
19071    /// Parse CACHE TABLE statement (Spark)
19072    /// CACHE [LAZY] TABLE name [OPTIONS(...)] [AS query]
19073    fn parse_cache(&mut self) -> Result<Expression> {
19074        self.expect(TokenType::Cache)?;
19075
19076        // Check for LAZY keyword
19077        let lazy = self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("LAZY");
19078        if lazy {
19079            self.skip();
19080        }
19081
19082        self.expect(TokenType::Table)?;
19083        let table = Identifier::new(self.expect_identifier()?);
19084
19085        // Check for OPTIONS clause
19086        let options =
19087            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTIONS") {
19088                self.skip();
19089                self.expect(TokenType::LParen)?;
19090                let mut opts = Vec::new();
19091                loop {
19092                    // Parse key = value pairs (key can be string literal or identifier)
19093                    let key = if self.check(TokenType::NationalString) {
19094                        let token = self.advance();
19095                        Expression::Literal(Box::new(Literal::NationalString(token.text)))
19096                    } else if self.check(TokenType::String) {
19097                        let token = self.advance();
19098                        Expression::Literal(Box::new(Literal::String(token.text)))
19099                    } else {
19100                        Expression::Identifier(Identifier::new(self.expect_identifier()?))
19101                    };
19102                    // Eq is optional - Spark allows space-separated key value pairs
19103                    // e.g., OPTIONS ('storageLevel' 'DISK_ONLY') or OPTIONS ('key' = 'value')
19104                    let _ = self.match_token(TokenType::Eq);
19105                    let value = self.parse_expression()?;
19106                    opts.push((key, value));
19107                    if !self.match_token(TokenType::Comma) {
19108                        break;
19109                    }
19110                }
19111                self.expect(TokenType::RParen)?;
19112                opts
19113            } else {
19114                Vec::new()
19115            };
19116
19117        // Check for AS clause or implicit query (SELECT without AS in Spark)
19118        let query = if self.match_token(TokenType::As) {
19119            Some(self.parse_statement()?)
19120        } else if self.check(TokenType::Select) || self.check(TokenType::With) {
19121            // Spark allows SELECT without AS keyword after CACHE TABLE
19122            Some(self.parse_statement()?)
19123        } else {
19124            None
19125        };
19126
19127        Ok(Expression::Cache(Box::new(Cache {
19128            table,
19129            lazy,
19130            options,
19131            query,
19132        })))
19133    }
19134
19135    /// Parse UNCACHE TABLE statement (Spark)
19136    /// UNCACHE TABLE [IF EXISTS] name
19137    fn parse_uncache(&mut self) -> Result<Expression> {
19138        self.expect(TokenType::Uncache)?;
19139        self.expect(TokenType::Table)?;
19140
19141        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
19142        let table = Identifier::new(self.expect_identifier()?);
19143
19144        Ok(Expression::Uncache(Box::new(Uncache { table, if_exists })))
19145    }
19146
19147    /// Parse LOAD DATA statement (Hive)
19148    /// LOAD DATA [LOCAL] INPATH 'path' [OVERWRITE] INTO TABLE table_name
19149    /// [PARTITION (col=val, ...)] [INPUTFORMAT 'format'] [SERDE 'serde']
19150    fn parse_load_data(&mut self) -> Result<Expression> {
19151        self.expect(TokenType::Load)?;
19152
19153        // Expect DATA keyword
19154        let data_token = self.advance();
19155        if !data_token.text.eq_ignore_ascii_case("DATA") {
19156            return Err(self.parse_error("Expected DATA after LOAD"));
19157        }
19158
19159        // Check for LOCAL keyword
19160        let local = self.match_token(TokenType::Local);
19161
19162        // Expect INPATH
19163        self.expect(TokenType::Inpath)?;
19164
19165        // Parse the path (string literal)
19166        let inpath = if self.check(TokenType::String) {
19167            self.advance().text
19168        } else {
19169            return Err(self.parse_error("Expected string literal after INPATH"));
19170        };
19171
19172        // Check for OVERWRITE keyword
19173        let overwrite = self.match_token(TokenType::Overwrite);
19174
19175        // Expect INTO TABLE
19176        self.expect(TokenType::Into)?;
19177        self.expect(TokenType::Table)?;
19178
19179        // Parse table name (can be qualified)
19180        let table = Expression::Table(Box::new(self.parse_table_ref()?));
19181
19182        // Check for PARTITION clause
19183        let partition = if self.match_token(TokenType::Partition) {
19184            self.expect(TokenType::LParen)?;
19185            let mut partitions = Vec::new();
19186            loop {
19187                let col = Identifier::new(self.expect_identifier_or_keyword()?);
19188                self.expect(TokenType::Eq)?;
19189                let val = self.parse_expression()?;
19190                partitions.push((col, val));
19191                if !self.match_token(TokenType::Comma) {
19192                    break;
19193                }
19194            }
19195            self.expect(TokenType::RParen)?;
19196            partitions
19197        } else {
19198            Vec::new()
19199        };
19200
19201        // Check for INPUTFORMAT clause
19202        let input_format = if self.match_token(TokenType::InputFormat) {
19203            if self.check(TokenType::String) {
19204                Some(self.advance().text)
19205            } else {
19206                return Err(self.parse_error("Expected string literal after INPUTFORMAT"));
19207            }
19208        } else {
19209            None
19210        };
19211
19212        // Check for SERDE clause
19213        let serde = if self.match_token(TokenType::Serde) {
19214            if self.check(TokenType::String) {
19215                Some(self.advance().text)
19216            } else {
19217                return Err(self.parse_error("Expected string literal after SERDE"));
19218            }
19219        } else {
19220            None
19221        };
19222
19223        Ok(Expression::LoadData(Box::new(LoadData {
19224            local,
19225            inpath,
19226            overwrite,
19227            table,
19228            partition,
19229            input_format,
19230            serde,
19231        })))
19232    }
19233
19234    /// Parse PRAGMA statement (SQLite)
19235    /// PRAGMA [schema.]name [= value | (args...)]
19236    fn parse_pragma(&mut self) -> Result<Expression> {
19237        self.expect(TokenType::Pragma)?;
19238
19239        // Parse schema.name or just name
19240        let first_name = self.expect_identifier_or_keyword()?;
19241
19242        let (schema, name) = if self.match_token(TokenType::Dot) {
19243            // First name was schema
19244            let pragma_name = self.expect_identifier_or_keyword()?;
19245            (
19246                Some(Identifier::new(first_name)),
19247                Identifier::new(pragma_name),
19248            )
19249        } else {
19250            (None, Identifier::new(first_name))
19251        };
19252
19253        // Check for assignment or function call
19254        let (value, args) = if self.match_token(TokenType::Eq) {
19255            // PRAGMA name = value
19256            let val = self.parse_expression()?;
19257            (Some(val), Vec::new())
19258        } else if self.match_token(TokenType::LParen) {
19259            // PRAGMA name(args...)
19260            let mut arguments = Vec::new();
19261            if !self.check(TokenType::RParen) {
19262                loop {
19263                    arguments.push(self.parse_expression()?);
19264                    if !self.match_token(TokenType::Comma) {
19265                        break;
19266                    }
19267                }
19268            }
19269            self.expect(TokenType::RParen)?;
19270            (None, arguments)
19271        } else {
19272            (None, Vec::new())
19273        };
19274
19275        Ok(Expression::Pragma(Box::new(Pragma {
19276            schema,
19277            name,
19278            value,
19279            args,
19280        })))
19281    }
19282
19283    /// Parse ROLLBACK statement
19284    /// ROLLBACK [TO [SAVEPOINT] <name>]
19285    fn parse_rollback(&mut self) -> Result<Expression> {
19286        self.expect(TokenType::Rollback)?;
19287
19288        // Check for optional TRANSACTION, TRAN, or WORK keyword
19289        let has_transaction = self.match_token(TokenType::Transaction)
19290            || self.match_identifier("TRAN")
19291            || self.match_identifier("WORK");
19292
19293        // Check for TO SAVEPOINT (standard SQL) or transaction name (TSQL)
19294        let (savepoint, this) = if self.match_token(TokenType::To) {
19295            // Optional SAVEPOINT keyword
19296            self.match_token(TokenType::Savepoint);
19297            // Savepoint name
19298            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
19299                let name = self.advance().text;
19300                (
19301                    Some(Box::new(Expression::Identifier(Identifier::new(name)))),
19302                    None,
19303                )
19304            } else {
19305                (None, None)
19306            }
19307        } else if has_transaction
19308            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19309        {
19310            // TSQL: ROLLBACK TRANSACTION transaction_name
19311            let name = self.advance().text;
19312            (
19313                None,
19314                Some(Box::new(Expression::Identifier(Identifier::new(name)))),
19315            )
19316        } else if has_transaction {
19317            // Just ROLLBACK TRANSACTION - store marker
19318            (
19319                None,
19320                Some(Box::new(Expression::Identifier(Identifier::new(
19321                    "TRANSACTION".to_string(),
19322                )))),
19323            )
19324        } else {
19325            (None, None)
19326        };
19327
19328        Ok(Expression::Rollback(Box::new(Rollback { savepoint, this })))
19329    }
19330
19331    /// Parse COMMIT statement
19332    /// COMMIT [TRANSACTION|TRAN|WORK] [transaction_name] [WITH (DELAYED_DURABILITY = ON|OFF)] [AND [NO] CHAIN]
19333    fn parse_commit(&mut self) -> Result<Expression> {
19334        self.expect(TokenType::Commit)?;
19335
19336        // Check for optional TRANSACTION, TRAN, or WORK keyword
19337        let has_transaction = self.match_token(TokenType::Transaction)
19338            || self.match_identifier("TRAN")
19339            || self.match_identifier("WORK");
19340
19341        // Parse optional transaction name (TSQL)
19342        let this = if has_transaction
19343            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19344            && !self.check(TokenType::With)
19345            && !self.check(TokenType::And)
19346        {
19347            let name = self.advance().text;
19348            Some(Box::new(Expression::Identifier(Identifier::new(name))))
19349        } else if has_transaction {
19350            // Store marker that TRANSACTION keyword was present
19351            Some(Box::new(Expression::Identifier(Identifier::new(
19352                "TRANSACTION".to_string(),
19353            ))))
19354        } else {
19355            None
19356        };
19357
19358        // Parse WITH (DELAYED_DURABILITY = ON|OFF) for TSQL
19359        let durability = if self.match_token(TokenType::With) && self.match_token(TokenType::LParen)
19360        {
19361            // Check for DELAYED_DURABILITY
19362            if self.match_identifier("DELAYED_DURABILITY") && self.match_token(TokenType::Eq) {
19363                // ON is a keyword (TokenType::On), OFF is an identifier
19364                let on = self.match_token(TokenType::On) || self.match_identifier("ON");
19365                if !on {
19366                    self.match_identifier("OFF");
19367                }
19368                self.expect(TokenType::RParen)?;
19369                Some(Box::new(Expression::Boolean(BooleanLiteral { value: on })))
19370            } else {
19371                // Skip to RParen
19372                while !self.check(TokenType::RParen) && !self.is_at_end() {
19373                    self.skip();
19374                }
19375                self.match_token(TokenType::RParen);
19376                None
19377            }
19378        } else {
19379            None
19380        };
19381
19382        // Parse AND [NO] CHAIN
19383        let chain = if self.match_token(TokenType::And) {
19384            let no_chain = self.match_token(TokenType::No);
19385            self.match_identifier("CHAIN");
19386            if no_chain {
19387                // AND NO CHAIN - explicit false
19388                Some(Box::new(Expression::Boolean(BooleanLiteral {
19389                    value: false,
19390                })))
19391            } else {
19392                // AND CHAIN - explicit true
19393                Some(Box::new(Expression::Boolean(BooleanLiteral {
19394                    value: true,
19395                })))
19396            }
19397        } else {
19398            None
19399        };
19400
19401        Ok(Expression::Commit(Box::new(Commit {
19402            chain,
19403            this,
19404            durability,
19405        })))
19406    }
19407
19408    /// Parse END statement (PostgreSQL alias for COMMIT)
19409    /// END [WORK|TRANSACTION] [AND [NO] CHAIN]
19410    fn parse_end_transaction(&mut self) -> Result<Expression> {
19411        self.expect(TokenType::End)?;
19412
19413        // Check for optional WORK or TRANSACTION keyword
19414        let _has_work = self.match_identifier("WORK") || self.match_token(TokenType::Transaction);
19415
19416        // Parse AND [NO] CHAIN
19417        let chain = if self.match_token(TokenType::And) {
19418            let no_chain = self.match_token(TokenType::No);
19419            self.match_identifier("CHAIN");
19420            if no_chain {
19421                // AND NO CHAIN - explicit false
19422                Some(Box::new(Expression::Boolean(BooleanLiteral {
19423                    value: false,
19424                })))
19425            } else {
19426                // AND CHAIN - explicit true
19427                Some(Box::new(Expression::Boolean(BooleanLiteral {
19428                    value: true,
19429                })))
19430            }
19431        } else {
19432            None
19433        };
19434
19435        // Return as COMMIT since END is an alias
19436        Ok(Expression::Commit(Box::new(Commit {
19437            chain,
19438            this: None,
19439            durability: None,
19440        })))
19441    }
19442
19443    /// Parse BEGIN/START TRANSACTION statement
19444    /// BEGIN [DEFERRED|IMMEDIATE|EXCLUSIVE] [TRANSACTION|TRAN|WORK] [transaction_name] [WITH MARK 'description']
19445    /// Also handles procedural BEGIN blocks (BigQuery, etc.): BEGIN statement_list END
19446    fn parse_transaction(&mut self) -> Result<Expression> {
19447        self.expect(TokenType::Begin)?;
19448
19449        // Check if this is a procedural BEGIN block rather than a transaction
19450        // If next token is not a transaction keyword and we have more tokens, it's a procedural block
19451        let is_transaction = self.is_at_end()
19452            || self.check(TokenType::Semicolon)
19453            || self.check(TokenType::Transaction)
19454            || self.check_identifier("TRAN")
19455            || self.check_identifier("WORK")
19456            || self.check_identifier("DEFERRED")
19457            || self.check_identifier("IMMEDIATE")
19458            || self.check_identifier("EXCLUSIVE");
19459
19460        if !is_transaction {
19461            // TSQL: BEGIN TRY ... END TRY [BEGIN CATCH ... END CATCH]
19462            // These are block-structured constructs that may contain semicolons,
19463            // so we can't use parse_command() which stops at the first semicolon.
19464            let is_try = self.check_identifier("TRY");
19465            let is_catch = self.check_identifier("CATCH");
19466            if is_try || is_catch {
19467                let block_kind = if is_try { "TRY" } else { "CATCH" };
19468                self.skip(); // consume TRY or CATCH
19469                let mut tokens: Vec<(String, TokenType)> = vec![
19470                    ("BEGIN".to_string(), TokenType::Begin),
19471                    (block_kind.to_string(), TokenType::Var),
19472                ];
19473                // Collect tokens until matching END TRY / END CATCH
19474                while !self.is_at_end() {
19475                    if self.check(TokenType::End)
19476                        && self.current + 1 < self.tokens.len()
19477                        && self.tokens[self.current + 1]
19478                            .text
19479                            .eq_ignore_ascii_case(block_kind)
19480                    {
19481                        tokens.push(("END".to_string(), TokenType::End));
19482                        self.skip(); // consume END
19483                        tokens.push((block_kind.to_string(), TokenType::Var));
19484                        self.skip(); // consume TRY/CATCH
19485                        break;
19486                    }
19487                    let token = self.advance();
19488                    let text = if token.token_type == TokenType::String {
19489                        format!("'{}'", token.text)
19490                    } else if token.token_type == TokenType::QuotedIdentifier {
19491                        format!("\"{}\"", token.text)
19492                    } else {
19493                        token.text.clone()
19494                    };
19495                    tokens.push((text, token.token_type));
19496                }
19497                let mut result = Expression::Command(Box::new(Command {
19498                    this: self.join_command_tokens(tokens),
19499                }));
19500
19501                // If this was a TRY block, check for a following BEGIN CATCH block
19502                if is_try
19503                    && self.check(TokenType::Begin)
19504                    && self.current + 1 < self.tokens.len()
19505                    && self.tokens[self.current + 1]
19506                        .text
19507                        .eq_ignore_ascii_case("CATCH")
19508                {
19509                    // Recursively parse the BEGIN CATCH block
19510                    let catch_block = self.parse_transaction()?;
19511                    // Combine TRY and CATCH into a single command
19512                    if let (Expression::Command(try_cmd), Expression::Command(catch_cmd)) =
19513                        (&result, &catch_block)
19514                    {
19515                        result = Expression::Command(Box::new(Command {
19516                            this: format!("{} {}", try_cmd.this, catch_cmd.this),
19517                        }));
19518                    }
19519                }
19520
19521                return Ok(result);
19522            }
19523
19524            // This is a procedural BEGIN block - parse as Command
19525            // Collect remaining tokens until end of statement
19526            return self
19527                .parse_command()?
19528                .ok_or_else(|| self.parse_error("Failed to parse BEGIN block"));
19529        }
19530
19531        // Check for transaction kind: DEFERRED, IMMEDIATE, EXCLUSIVE (SQLite)
19532        let kind = if self.match_identifier("DEFERRED")
19533            || self.match_identifier("IMMEDIATE")
19534            || self.match_identifier("EXCLUSIVE")
19535        {
19536            Some(self.previous().text.clone())
19537        } else {
19538            None
19539        };
19540
19541        // Check for TRANSACTION, TRAN, or WORK keyword
19542        let has_transaction_keyword = self.match_token(TokenType::Transaction)
19543            || self.match_identifier("TRAN")
19544            || self.match_identifier("WORK");
19545
19546        // Parse optional transaction name (TSQL style: BEGIN TRANSACTION trans_name)
19547        let trans_name = if has_transaction_keyword
19548            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19549            && !self.check(TokenType::With)
19550        {
19551            // Could be a transaction name or @variable
19552            let name = self.advance().text;
19553            Some(name)
19554        } else {
19555            None
19556        };
19557
19558        // Combine kind and trans_name into `this`
19559        let this = if let Some(name) = trans_name {
19560            Some(Box::new(Expression::Identifier(Identifier::new(name))))
19561        } else if let Some(k) = kind {
19562            Some(Box::new(Expression::Identifier(Identifier::new(k))))
19563        } else {
19564            None
19565        };
19566
19567        // Parse WITH MARK 'description' (TSQL)
19568        let mark = if self.match_token(TokenType::With) && self.match_identifier("MARK") {
19569            if self.check(TokenType::String) {
19570                let desc = self.advance().text;
19571                Some(Box::new(Expression::Literal(Box::new(Literal::String(
19572                    desc,
19573                )))))
19574            } else {
19575                Some(Box::new(Expression::Literal(Box::new(Literal::String(
19576                    "".to_string(),
19577                )))))
19578            }
19579        } else if has_transaction_keyword {
19580            // Store "TRANSACTION" marker to preserve round-trip
19581            Some(Box::new(Expression::Identifier(Identifier::new(
19582                "TRANSACTION".to_string(),
19583            ))))
19584        } else {
19585            None
19586        };
19587
19588        // Parse any additional transaction modes (isolation levels, etc.)
19589        let mut mode_parts: Vec<String> = Vec::new();
19590        while self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
19591            let mut mode_tokens: Vec<String> = Vec::new();
19592            while (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19593                && !self.check(TokenType::Comma)
19594            {
19595                mode_tokens.push(self.advance().text);
19596            }
19597            if !mode_tokens.is_empty() {
19598                mode_parts.push(mode_tokens.join(" "));
19599            }
19600            if !self.match_token(TokenType::Comma) {
19601                break;
19602            }
19603        }
19604
19605        let modes = if !mode_parts.is_empty() {
19606            Some(Box::new(Expression::Identifier(Identifier::new(
19607                mode_parts.join(", "),
19608            ))))
19609        } else {
19610            None
19611        };
19612
19613        Ok(Expression::Transaction(Box::new(Transaction {
19614            this,
19615            modes,
19616            mark,
19617        })))
19618    }
19619
19620    /// Parse START TRANSACTION statement
19621    /// START TRANSACTION [READ ONLY | READ WRITE] [, ISOLATION LEVEL ...]
19622    fn parse_start_transaction(&mut self) -> Result<Expression> {
19623        self.expect(TokenType::Start)?;
19624
19625        // Expect TRANSACTION keyword
19626        self.expect(TokenType::Transaction)?;
19627
19628        // Parse any transaction modes (READ ONLY, READ WRITE, ISOLATION LEVEL, etc.)
19629        let mut mode_parts: Vec<String> = Vec::new();
19630        while self.is_identifier_token()
19631            || self.is_safe_keyword_as_identifier()
19632            || self.match_identifier("READ")
19633        {
19634            // If we matched READ, add it to tokens
19635            let read_matched = if self.previous().text.eq_ignore_ascii_case("READ") {
19636                true
19637            } else {
19638                false
19639            };
19640            let mut mode_tokens: Vec<String> = Vec::new();
19641            if read_matched {
19642                mode_tokens.push("READ".to_string());
19643            }
19644            while (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19645                && !self.check(TokenType::Comma)
19646            {
19647                mode_tokens.push(self.advance().text);
19648            }
19649            if !mode_tokens.is_empty() {
19650                mode_parts.push(mode_tokens.join(" "));
19651            }
19652            if !self.match_token(TokenType::Comma) {
19653                break;
19654            }
19655        }
19656
19657        let modes = if !mode_parts.is_empty() {
19658            Some(Box::new(Expression::Identifier(Identifier::new(
19659                mode_parts.join(", "),
19660            ))))
19661        } else {
19662            None
19663        };
19664
19665        Ok(Expression::Transaction(Box::new(Transaction {
19666            this: None, // START TRANSACTION doesn't have a kind like DEFERRED/IMMEDIATE
19667            modes,
19668            // Mark as START to differentiate from BEGIN
19669            mark: Some(Box::new(Expression::Identifier(Identifier::new(
19670                "START".to_string(),
19671            )))),
19672        })))
19673    }
19674
19675    /// Parse DESCRIBE statement
19676    /// DESCRIBE [EXTENDED|FORMATTED|ANALYZE] <table_or_query>
19677    /// Also handles EXPLAIN (parsed as Describe)
19678    fn parse_describe(&mut self) -> Result<Expression> {
19679        // Accept DESCRIBE, DESC, and EXPLAIN (Var token)
19680        // Capture leading comments from the first token
19681        let leading_comments = if self.check(TokenType::Describe) {
19682            let token = self.advance();
19683            token.comments
19684        } else if self.check(TokenType::Desc) {
19685            let token = self.advance();
19686            token.comments
19687        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EXPLAIN") {
19688            let token = self.advance(); // consume EXPLAIN
19689            token.comments
19690        } else {
19691            return Err(self.parse_error("Expected DESCRIBE, DESC, or EXPLAIN"));
19692        };
19693
19694        // Check for EXTENDED or FORMATTED keywords
19695        let extended = self.match_identifier("EXTENDED");
19696        let formatted = if !extended {
19697            self.match_identifier("FORMATTED")
19698        } else {
19699            false
19700        };
19701
19702        // Check for style keywords like ANALYZE, HISTORY
19703        // ClickHouse: EXPLAIN SYNTAX/AST/PLAN/PIPELINE/ESTIMATE/TABLE OVERRIDE/CURRENT TRANSACTION
19704        // For HISTORY, we need to look ahead to ensure it's not part of a schema-qualified
19705        // table name like "history.tbl". If the next token is a Dot, "history" is a schema name.
19706        let style = if !extended && !formatted && self.match_identifier("ANALYZE") {
19707            Some("ANALYZE".to_string())
19708        } else if !extended
19709            && !formatted
19710            && matches!(
19711                self.config.dialect,
19712                Some(crate::dialects::DialectType::ClickHouse)
19713            )
19714        {
19715            // ClickHouse EXPLAIN styles
19716            let text_upper = if !self.is_at_end() {
19717                self.peek().text.to_ascii_uppercase()
19718            } else {
19719                String::new()
19720            };
19721            match text_upper.as_str() {
19722                "SYNTAX" | "AST" | "PLAN" | "PIPELINE" | "ESTIMATE" | "QUERY" | "CURRENT" => {
19723                    self.skip();
19724                    let mut style_str = text_upper;
19725                    // Handle multi-word: TABLE OVERRIDE, CURRENT TRANSACTION, QUERY TREE
19726                    if style_str == "CURRENT" && self.check_identifier("TRANSACTION") {
19727                        style_str.push_str(" TRANSACTION");
19728                        self.skip();
19729                    }
19730                    if style_str == "QUERY" && self.check_identifier("TREE") {
19731                        style_str.push_str(" TREE");
19732                        self.skip();
19733                    }
19734                    Some(style_str)
19735                }
19736                _ if self.check(TokenType::Table) => {
19737                    // EXPLAIN TABLE OVERRIDE
19738                    self.skip(); // consume TABLE
19739                    if self.check_identifier("OVERRIDE") {
19740                        self.skip();
19741                        Some("TABLE OVERRIDE".to_string())
19742                    } else {
19743                        // Not TABLE OVERRIDE, backtrack
19744                        self.current -= 1;
19745                        None
19746                    }
19747                }
19748                _ => None,
19749            }
19750        } else if !extended
19751            && !formatted
19752            && (self.check(TokenType::Identifier)
19753                || self.check(TokenType::Var)
19754                || self.check(TokenType::QuotedIdentifier))
19755            && self.peek().text.eq_ignore_ascii_case("HISTORY")
19756            && self.peek_nth(1).map(|t| t.token_type) != Some(TokenType::Dot)
19757        {
19758            self.skip(); // consume HISTORY
19759            Some("HISTORY".to_string())
19760        } else {
19761            None
19762        };
19763
19764        // Check for object kind like SEMANTIC VIEW, TABLE, INPUT, OUTPUT, etc.
19765        let kind = if self.match_identifier("SEMANTIC") {
19766            if self.match_token(TokenType::View) {
19767                Some("SEMANTIC VIEW".to_string())
19768            } else {
19769                Some("SEMANTIC".to_string())
19770            }
19771        } else if self.match_token(TokenType::Table) {
19772            Some("TABLE".to_string())
19773        } else if self.match_token(TokenType::View) {
19774            Some("VIEW".to_string())
19775        } else if self.match_identifier("DATABASE") {
19776            Some("DATABASE".to_string())
19777        } else if self.match_identifier("SCHEMA") {
19778            Some("SCHEMA".to_string())
19779        } else if self.match_token(TokenType::Input) {
19780            Some("INPUT".to_string())
19781        } else if self.match_token(TokenType::Output) {
19782            Some("OUTPUT".to_string())
19783        } else {
19784            None
19785        };
19786
19787        // ClickHouse: parse EXPLAIN settings before the target statement
19788        // e.g., EXPLAIN actions=1, description=0 SELECT ...
19789        // e.g., EXPLAIN PLAN actions=1 SELECT ...
19790        let mut properties = Vec::new();
19791        if matches!(
19792            self.config.dialect,
19793            Some(crate::dialects::DialectType::ClickHouse)
19794        ) {
19795            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
19796                // Look for key=value pairs before a statement keyword
19797                if (self.is_identifier_token()
19798                    || self.is_safe_keyword_as_identifier()
19799                    || self.check(TokenType::Type))
19800                    && self.current + 1 < self.tokens.len()
19801                    && self.tokens[self.current + 1].token_type == TokenType::Eq
19802                {
19803                    let name = self.advance().text.to_lowercase();
19804                    self.skip(); // consume =
19805                    let value = self.advance().text.clone();
19806                    properties.push((name, value));
19807                    self.match_token(TokenType::Comma); // optional comma between settings
19808                } else {
19809                    break;
19810                }
19811            }
19812        }
19813
19814        // Parse target - could be a table name or a SELECT/INSERT/other statement
19815        // ClickHouse: EXPLAIN/DESC can precede any statement or subquery
19816        let target = if self.check(TokenType::Select) || self.check(TokenType::With) {
19817            self.parse_statement()?
19818        } else if self.check(TokenType::LParen) && {
19819            // Look through nested parens for SELECT/WITH
19820            let mut depth = 0usize;
19821            let mut found_select = false;
19822            for i in 0..100 {
19823                match self.peek_nth(i).map(|t| t.token_type) {
19824                    Some(TokenType::LParen) => depth += 1,
19825                    Some(TokenType::Select) | Some(TokenType::With) if depth > 0 => {
19826                        found_select = true;
19827                        break;
19828                    }
19829                    _ => break,
19830                }
19831            }
19832            found_select
19833        } {
19834            // DESC (((SELECT ...))) — deeply nested parenthesized subquery
19835            self.parse_statement()?
19836        } else if matches!(
19837            self.config.dialect,
19838            Some(crate::dialects::DialectType::ClickHouse)
19839        ) && (self.check(TokenType::Insert)
19840            || self.check(TokenType::Create)
19841            || self.check(TokenType::Alter)
19842            || self.check(TokenType::Drop)
19843            || self.check(TokenType::Set)
19844            || self.check(TokenType::System))
19845        {
19846            self.parse_statement()?
19847        } else if matches!(
19848            self.config.dialect,
19849            Some(crate::dialects::DialectType::ClickHouse)
19850        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19851            && self.peek_nth(1).map(|t| t.token_type) == Some(TokenType::LParen)
19852        {
19853            // ClickHouse: DESC format(Values, '(123)') — function call as target
19854            self.parse_expression()?
19855        } else {
19856            // Parse as table reference
19857            let table = self.parse_table_ref()?;
19858            Expression::Table(Box::new(table))
19859        };
19860
19861        // Parse optional PARTITION clause (Spark/Hive)
19862        let partition = if self.match_token(TokenType::Partition) {
19863            // PARTITION(key = value, ...)
19864            self.expect(TokenType::LParen)?;
19865            // Parse partition expressions (e.g., ds = '2024-01-01')
19866            let mut partition_exprs = Vec::new();
19867            loop {
19868                if let Some(expr) = self.parse_conjunction()? {
19869                    partition_exprs.push(expr);
19870                }
19871                if !self.match_token(TokenType::Comma) {
19872                    break;
19873                }
19874            }
19875            self.expect(TokenType::RParen)?;
19876            let partition = Expression::Partition(Box::new(crate::expressions::Partition {
19877                expressions: partition_exprs,
19878                subpartition: false,
19879            }));
19880            Some(Box::new(partition))
19881        } else {
19882            None
19883        };
19884
19885        // ClickHouse: consume optional SETTINGS clause after target
19886        // e.g., DESC format(CSV, '...') SETTINGS key='val', key2='val2'
19887        if matches!(
19888            self.config.dialect,
19889            Some(crate::dialects::DialectType::ClickHouse)
19890        ) && self.check(TokenType::Settings)
19891        {
19892            self.skip(); // consume SETTINGS
19893            let _ = self.parse_settings_property()?;
19894        }
19895
19896        // Databricks: DESCRIBE ... AS JSON
19897        let as_json = if self.check(TokenType::As)
19898            && self
19899                .peek_nth(1)
19900                .map(|t| t.text.eq_ignore_ascii_case("JSON"))
19901                == Some(true)
19902        {
19903            self.skip(); // consume AS
19904            self.skip(); // consume JSON
19905            true
19906        } else {
19907            false
19908        };
19909
19910        // Parse optional post-target properties like type=stage (non-ClickHouse)
19911        if properties.is_empty() {
19912            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
19913                // Check for identifier or keyword that could be a property name
19914                if self.check(TokenType::Var) || self.check(TokenType::Type) || self.check_keyword()
19915                {
19916                    let name = self.advance().text.to_lowercase();
19917                    if self.match_token(TokenType::Eq) {
19918                        let value = self.advance().text.clone();
19919                        properties.push((name, value));
19920                    } else {
19921                        // Not a property, put it back (can't easily undo, so break)
19922                        break;
19923                    }
19924                } else {
19925                    break;
19926                }
19927            }
19928        }
19929
19930        Ok(Expression::Describe(Box::new(Describe {
19931            target,
19932            extended,
19933            formatted,
19934            kind,
19935            properties,
19936            style,
19937            partition,
19938            leading_comments,
19939            as_json,
19940        })))
19941    }
19942
19943    /// Parse SHOW statement
19944    /// SHOW [TERSE] <object_type> [HISTORY] [LIKE pattern] [IN <scope>] [STARTS WITH pattern] [LIMIT n] [FROM object]
19945    fn parse_show(&mut self) -> Result<Expression> {
19946        self.expect(TokenType::Show)?;
19947
19948        // Check for TERSE
19949        let terse = self.match_identifier("TERSE");
19950
19951        // Parse the thing to show (DATABASES, TABLES, SCHEMAS, etc.)
19952        // This can be multiple words like "PRIMARY KEYS" or "IMPORTED KEYS"
19953        let mut this_parts = Vec::new();
19954        let mut target: Option<Expression> = None;
19955        let mut mutex: Option<bool> = None;
19956
19957        // Consume identifier tokens until we hit a keyword like LIKE, IN, FROM, LIMIT, HISTORY
19958        // Special handling for SingleStore SHOW variations
19959        while !self.is_at_end() {
19960            let current = self.peek();
19961            // Stop at keywords that start clauses
19962            if matches!(
19963                current.token_type,
19964                TokenType::Like
19965                    | TokenType::In
19966                    | TokenType::From
19967                    | TokenType::Limit
19968                    | TokenType::Semicolon
19969                    | TokenType::Eof
19970                    | TokenType::Where
19971                    | TokenType::For
19972                    | TokenType::Offset
19973                    | TokenType::Settings
19974            ) {
19975                // ClickHouse: SHOW CREATE SETTINGS PROFILE - don't stop at SETTINGS
19976                if current.token_type == TokenType::Settings
19977                    && matches!(
19978                        self.config.dialect,
19979                        Some(crate::dialects::DialectType::ClickHouse)
19980                    )
19981                    && this_parts.join(" ") == "CREATE"
19982                {
19983                    // Fall through to process SETTINGS as part of the type name
19984                } else {
19985                    break;
19986                }
19987            }
19988            // Handle comma-separated profile types (e.g., SHOW PROFILE BLOCK IO, PAGE FAULTS)
19989            // Append comma to the last part to preserve spacing
19990            if current.token_type == TokenType::Comma {
19991                if !this_parts.is_empty() {
19992                    let last = this_parts.pop().unwrap();
19993                    this_parts.push(format!("{},", last));
19994                }
19995                self.skip();
19996                continue;
19997            }
19998            // Stop at HISTORY keyword (but not as the first word)
19999            if !this_parts.is_empty() && current.text.eq_ignore_ascii_case("HISTORY") {
20000                break;
20001            }
20002            // Stop at STARTS keyword
20003            if current.text.eq_ignore_ascii_case("STARTS") {
20004                break;
20005            }
20006            // SingleStore: SHOW PLAN <id> - handle number directly (before Var/keyword check)
20007            // This is needed because numbers don't pass the Var/keyword check
20008            let joined_check = this_parts.join(" ");
20009            if joined_check == "PLAN" && current.token_type == TokenType::Number {
20010                let id = self.advance().text;
20011                target = Some(Expression::Literal(Box::new(Literal::Number(id))));
20012                break;
20013            }
20014            // Accept identifiers and keywords as part of the object type
20015            if current.token_type == TokenType::Var || current.token_type.is_keyword() {
20016                let joined = this_parts.join(" ");
20017
20018                // SingleStore: SHOW CREATE <type> <name> - preserve case for name
20019                // Types: AGGREGATE, PIPELINE, PROJECTION
20020                if matches!(
20021                    joined.as_str(),
20022                    "CREATE AGGREGATE" | "CREATE PIPELINE" | "CREATE PROJECTION"
20023                ) {
20024                    let name = self.advance().text;
20025                    target = Some(Expression::Identifier(Identifier::new(name)));
20026                    break;
20027                }
20028
20029                // SingleStore: SHOW <type> ON <name> - preserve case for name after ON
20030                // Check if current token is "ON" (but not at start)
20031                if current.text.eq_ignore_ascii_case("ON") && !this_parts.is_empty() {
20032                    this_parts.push("ON".to_string());
20033                    self.skip();
20034                    // Parse the name after ON, preserving case
20035                    if !self.is_at_end() {
20036                        let next = self.peek();
20037                        // Handle "ON TABLE name" pattern
20038                        if next.text.eq_ignore_ascii_case("TABLE") {
20039                            this_parts.push("TABLE".to_string());
20040                            self.skip();
20041                        }
20042                        // Parse the actual name
20043                        if !self.is_at_end() {
20044                            let name_tok = self.peek();
20045                            if name_tok.token_type == TokenType::Var
20046                                || name_tok.token_type.is_keyword()
20047                            {
20048                                let name = self.advance().text;
20049                                target = Some(Expression::Identifier(Identifier::new(name)));
20050                            }
20051                        }
20052                    }
20053                    break;
20054                }
20055
20056                // SingleStore: SHOW REPRODUCTION INTO OUTFILE 'filename'
20057                if current.text.eq_ignore_ascii_case("INTO") && joined == "REPRODUCTION" {
20058                    this_parts.push("INTO".to_string());
20059                    self.skip();
20060                    if !self.is_at_end() && self.peek().text.eq_ignore_ascii_case("OUTFILE") {
20061                        this_parts.push("OUTFILE".to_string());
20062                        self.skip();
20063                        // Parse the filename
20064                        if !self.is_at_end() && self.check(TokenType::String) {
20065                            let filename = self.advance().text;
20066                            target = Some(Expression::Literal(Box::new(Literal::String(filename))));
20067                        }
20068                    }
20069                    break;
20070                }
20071
20072                // SingleStore: SHOW PLAN [JSON] <id> - capture the numeric ID
20073                if joined == "PLAN" {
20074                    // Check if current is "JSON" - if so, push it and check for number
20075                    if current.text.eq_ignore_ascii_case("JSON") {
20076                        this_parts.push("JSON".to_string());
20077                        self.skip();
20078                        // Now check for number
20079                        if !self.is_at_end() && self.check(TokenType::Number) {
20080                            let id = self.advance().text;
20081                            target = Some(Expression::Literal(Box::new(Literal::Number(id))));
20082                        }
20083                        break;
20084                    }
20085                    // Check if current is a number (plan ID)
20086                    if current.token_type == TokenType::Number {
20087                        let id = self.advance().text;
20088                        target = Some(Expression::Literal(Box::new(Literal::Number(id))));
20089                        break;
20090                    }
20091                }
20092
20093                this_parts.push(current.text.to_ascii_uppercase());
20094                self.skip();
20095
20096                // ClickHouse: SHOW CREATE TABLE/VIEW/DICTIONARY <qualified_name>
20097                // After detecting CREATE TABLE/VIEW/DICTIONARY, parse the next as a table ref
20098                let joined = this_parts.join(" ");
20099                if matches!(
20100                    joined.as_str(),
20101                    "CREATE TABLE"
20102                        | "CREATE VIEW"
20103                        | "CREATE DICTIONARY"
20104                        | "CREATE DATABASE"
20105                        | "CREATE MATERIALIZED VIEW"
20106                        | "CREATE LIVE VIEW"
20107                ) {
20108                    if !self.is_at_end()
20109                        && (self.check(TokenType::Var)
20110                            || self.check(TokenType::QuotedIdentifier)
20111                            || self.is_safe_keyword_as_identifier())
20112                    {
20113                        let table = self.parse_table_ref()?;
20114                        target = Some(Expression::Table(Box::new(table)));
20115                    }
20116                    break;
20117                }
20118
20119                // ClickHouse: SHOW CREATE ROLE/PROFILE/QUOTA/ROW POLICY/POLICY with multi-name or ON clause
20120                // These have complex syntax (comma-separated names, ON db.table) - consume as raw text
20121                if matches!(
20122                    self.config.dialect,
20123                    Some(crate::dialects::DialectType::ClickHouse)
20124                ) && (matches!(
20125                    joined.as_str(),
20126                    "CREATE ROLE"
20127                        | "CREATE QUOTA"
20128                        | "CREATE SETTINGS PROFILE"
20129                        | "CREATE PROFILE"
20130                        | "CREATE ROW POLICY"
20131                        | "CREATE POLICY"
20132                        | "CREATE USER"
20133                ) || matches!(
20134                    joined.as_str(),
20135                    "SHOW CREATE ROLE"
20136                        | "SHOW CREATE QUOTA"
20137                        | "SHOW CREATE SETTINGS PROFILE"
20138                        | "SHOW CREATE PROFILE"
20139                        | "SHOW CREATE ROW POLICY"
20140                        | "SHOW CREATE POLICY"
20141                        | "SHOW CREATE USER"
20142                )) {
20143                    let mut parts = Vec::new();
20144                    while !self.is_at_end() && self.peek().token_type != TokenType::Semicolon {
20145                        parts.push(self.advance().text.clone());
20146                    }
20147                    target = Some(Expression::Identifier(Identifier::new(parts.join(" "))));
20148                    break;
20149                }
20150
20151                // ClickHouse: SHOW CREATE <qualified_name> (without TABLE/VIEW keyword)
20152                // e.g., SHOW CREATE INFORMATION_SCHEMA.COLUMNS
20153                if joined == "CREATE"
20154                    && matches!(
20155                        self.config.dialect,
20156                        Some(crate::dialects::DialectType::ClickHouse)
20157                    )
20158                    && !self.is_at_end()
20159                    && (self.check(TokenType::Var) || self.check(TokenType::QuotedIdentifier))
20160                    && !matches!(
20161                        self.peek().text.to_ascii_uppercase().as_str(),
20162                        "TABLE"
20163                            | "VIEW"
20164                            | "DICTIONARY"
20165                            | "DATABASE"
20166                            | "MATERIALIZED"
20167                            | "LIVE"
20168                            | "TEMPORARY"
20169                            | "ROLE"
20170                            | "QUOTA"
20171                            | "POLICY"
20172                            | "PROFILE"
20173                            | "USER"
20174                            | "ROW"
20175                            | "SETTINGS"
20176                    )
20177                {
20178                    let table = self.parse_table_ref()?;
20179                    target = Some(Expression::Table(Box::new(table)));
20180                    break;
20181                }
20182
20183                // Special handling for ENGINE: the next token is the engine name (case-preserved)
20184                // followed by STATUS or MUTEX
20185                if joined == "ENGINE" {
20186                    // Parse engine name (case-preserved)
20187                    if !self.is_at_end() {
20188                        let engine_tok = self.peek();
20189                        if engine_tok.token_type == TokenType::Var
20190                            || engine_tok.token_type.is_keyword()
20191                        {
20192                            let engine_name = self.advance().text;
20193                            target = Some(Expression::Identifier(Identifier::new(engine_name)));
20194                            // Parse STATUS or MUTEX
20195                            if !self.is_at_end() {
20196                                let next = self.peek();
20197                                let next_upper = next.text.to_ascii_uppercase();
20198                                if next_upper == "STATUS" {
20199                                    self.skip();
20200                                    mutex = Some(false);
20201                                } else if next_upper == "MUTEX" {
20202                                    self.skip();
20203                                    mutex = Some(true);
20204                                }
20205                            }
20206                        }
20207                    }
20208                    break;
20209                }
20210            } else {
20211                break;
20212            }
20213        }
20214
20215        let this = this_parts.join(" ");
20216
20217        // Check for HISTORY
20218        let history = self.match_identifier("HISTORY");
20219
20220        // Check for FOR target (MySQL: SHOW GRANTS FOR foo, SHOW PROFILE ... FOR QUERY 5)
20221        // SingleStore: SHOW GROUPS FOR ROLE 'role_name', SHOW GROUPS FOR USER 'username'
20222        let for_target = if self.match_token(TokenType::For) {
20223            // Parse the target (can be multi-word like QUERY 5, or ROLE 'name')
20224            let mut parts = Vec::new();
20225            while !self.is_at_end() {
20226                let tok = self.peek();
20227                if matches!(
20228                    tok.token_type,
20229                    TokenType::Like
20230                        | TokenType::In
20231                        | TokenType::From
20232                        | TokenType::Limit
20233                        | TokenType::Semicolon
20234                        | TokenType::Eof
20235                        | TokenType::Where
20236                ) {
20237                    break;
20238                }
20239                if tok.token_type == TokenType::Var
20240                    || tok.token_type.is_keyword()
20241                    || tok.token_type == TokenType::Number
20242                {
20243                    parts.push(self.advance().text);
20244                } else if tok.token_type == TokenType::String {
20245                    // Handle string literals (e.g., SHOW GROUPS FOR ROLE 'role_name')
20246                    let text = self.advance().text;
20247                    parts.push(format!("'{}'", text));
20248                } else {
20249                    break;
20250                }
20251            }
20252            if parts.is_empty() {
20253                None
20254            } else {
20255                Some(Expression::Identifier(Identifier::new(parts.join(" "))))
20256            }
20257        } else {
20258            None
20259        };
20260
20261        // Check for LIKE pattern
20262        let like = if self.match_token(TokenType::Like) {
20263            Some(self.parse_primary()?)
20264        } else {
20265            None
20266        };
20267
20268        // Check for IN scope
20269        let (scope_kind, scope) = if self.match_token(TokenType::In) {
20270            // Parse scope kind and optionally scope object
20271            // Check for keywords: ACCOUNT, DATABASE, SCHEMA, TABLE, CLASS, APPLICATION
20272            let (kind, scope_obj) = if self.match_keyword("ACCOUNT") {
20273                (Some("ACCOUNT".to_string()), None)
20274            } else if self.match_token(TokenType::Database) {
20275                // IN DATABASE [name]
20276                let scope_obj = if !self.is_at_end()
20277                    && !self.check(TokenType::Like)
20278                    && !self.check(TokenType::Limit)
20279                    && !self.check(TokenType::Semicolon)
20280                    && !self.check_keyword_text("STARTS")
20281                {
20282                    let table = self.parse_table_ref()?;
20283                    Some(Expression::Table(Box::new(table)))
20284                } else {
20285                    None
20286                };
20287                (Some("DATABASE".to_string()), scope_obj)
20288            } else if self.match_token(TokenType::Schema) {
20289                // IN SCHEMA [name]
20290                let scope_obj = if !self.is_at_end()
20291                    && !self.check(TokenType::Like)
20292                    && !self.check(TokenType::Limit)
20293                    && !self.check(TokenType::Semicolon)
20294                    && !self.check_keyword_text("STARTS")
20295                {
20296                    let table = self.parse_table_ref()?;
20297                    Some(Expression::Table(Box::new(table)))
20298                } else {
20299                    None
20300                };
20301                (Some("SCHEMA".to_string()), scope_obj)
20302            } else if self.match_token(TokenType::Table) {
20303                // IN TABLE [name]
20304                let scope_obj = if !self.is_at_end()
20305                    && !self.check(TokenType::Like)
20306                    && !self.check(TokenType::Limit)
20307                    && !self.check(TokenType::Semicolon)
20308                    && !self.check_keyword_text("STARTS")
20309                {
20310                    let table = self.parse_table_ref()?;
20311                    Some(Expression::Table(Box::new(table)))
20312                } else {
20313                    None
20314                };
20315                (Some("TABLE".to_string()), scope_obj)
20316            } else if self.match_token(TokenType::View) {
20317                // IN VIEW [name]
20318                let scope_obj = if !self.is_at_end()
20319                    && !self.check(TokenType::Like)
20320                    && !self.check(TokenType::Limit)
20321                    && !self.check(TokenType::Semicolon)
20322                    && !self.check_keyword_text("STARTS")
20323                {
20324                    let table = self.parse_table_ref()?;
20325                    Some(Expression::Table(Box::new(table)))
20326                } else {
20327                    None
20328                };
20329                (Some("VIEW".to_string()), scope_obj)
20330            } else if self.match_keyword("CLASS") {
20331                // IN CLASS name
20332                let scope_obj = if !self.is_at_end() {
20333                    let table = self.parse_table_ref()?;
20334                    Some(Expression::Table(Box::new(table)))
20335                } else {
20336                    None
20337                };
20338                (Some("CLASS".to_string()), scope_obj)
20339            } else if self.match_keyword("APPLICATION") {
20340                // IN APPLICATION [PACKAGE] name
20341                let kind = if self.match_keyword("PACKAGE") {
20342                    "APPLICATION PACKAGE".to_string()
20343                } else {
20344                    "APPLICATION".to_string()
20345                };
20346                let scope_obj = if !self.is_at_end() {
20347                    let table = self.parse_table_ref()?;
20348                    Some(Expression::Table(Box::new(table)))
20349                } else {
20350                    None
20351                };
20352                (Some(kind), scope_obj)
20353            } else {
20354                // Default - infer scope_kind based on what we're showing
20355                // Python SQLGlot: SCHEMA_KINDS = {"OBJECTS", "TABLES", "VIEWS", "SEQUENCES", "UNIQUE KEYS", "IMPORTED KEYS"}
20356                let table = self.parse_table_ref()?;
20357                let inferred_kind = match this.as_str() {
20358                    "OBJECTS" | "TABLES" | "VIEWS" | "SEQUENCES" | "UNIQUE KEYS"
20359                    | "IMPORTED KEYS" => "SCHEMA",
20360                    "PRIMARY KEYS" => "TABLE",
20361                    _ => "SCHEMA", // Default to SCHEMA for unknown types
20362                };
20363                (
20364                    Some(inferred_kind.to_string()),
20365                    Some(Expression::Table(Box::new(table))),
20366                )
20367            };
20368            (kind, scope_obj)
20369        } else {
20370            (None, None)
20371        };
20372
20373        // Check for STARTS WITH
20374        let starts_with = if self.match_keyword("STARTS") {
20375            self.match_token(TokenType::With); // WITH is a keyword token
20376            Some(self.parse_primary()?)
20377        } else {
20378            None
20379        };
20380
20381        // Check for LIMIT
20382        let limit = if self.match_token(TokenType::Limit) {
20383            Some(Box::new(Limit {
20384                this: self.parse_expression()?,
20385                percent: false,
20386                comments: Vec::new(),
20387            }))
20388        } else {
20389            None
20390        };
20391
20392        // Check for FROM (can be a string literal or identifier)
20393        // For MySQL SHOW COLUMNS/INDEX, the first FROM is the target table,
20394        // and the second FROM is the database
20395        let mut from = if self.match_token(TokenType::From) {
20396            Some(self.parse_primary()?)
20397        } else {
20398            None
20399        };
20400
20401        // Check for second FROM clause (MySQL: SHOW COLUMNS FROM tbl FROM db, SHOW INDEX FROM foo FROM bar)
20402        let mut db = if from.is_some() && self.match_token(TokenType::From) {
20403            Some(self.parse_primary()?)
20404        } else {
20405            None
20406        };
20407
20408        // Normalize MySQL SHOW INDEX/COLUMNS FROM db.tbl -> FROM tbl FROM db.
20409        if matches!(this.as_str(), "INDEX" | "COLUMNS") && db.is_none() {
20410            if let Some(from_expr) = from.take() {
20411                match from_expr {
20412                    Expression::Table(mut t) => {
20413                        if let Some(db_ident) = t.schema.take().or(t.catalog.take()) {
20414                            db = Some(Expression::Identifier(db_ident));
20415                            from = Some(Expression::Identifier(t.name));
20416                        } else {
20417                            from = Some(Expression::Table(t));
20418                        }
20419                    }
20420                    Expression::Column(c) => {
20421                        if let Some(table_ident) = c.table {
20422                            db = Some(Expression::Identifier(table_ident));
20423                            from = Some(Expression::Identifier(c.name));
20424                        } else {
20425                            from = Some(Expression::Column(c));
20426                        }
20427                    }
20428                    Expression::Identifier(id) => {
20429                        if let Some((db_name, table_name)) = id.name.split_once('.') {
20430                            db = Some(Expression::Identifier(Identifier::new(db_name)));
20431                            from = Some(Expression::Identifier(Identifier {
20432                                name: table_name.to_string(),
20433                                quoted: id.quoted,
20434                                trailing_comments: id.trailing_comments,
20435                                span: None,
20436                            }));
20437                        } else {
20438                            from = Some(Expression::Identifier(id));
20439                        }
20440                    }
20441                    other => {
20442                        from = Some(other);
20443                    }
20444                }
20445            }
20446        }
20447
20448        // MySQL: SHOW TABLES FROM db LIKE 'pattern' (LIKE can come after FROM)
20449        let like = if like.is_none() && self.match_token(TokenType::Like) {
20450            Some(self.parse_primary()?)
20451        } else {
20452            like
20453        };
20454
20455        // ClickHouse: SHOW ... NOT LIKE 'pattern' / NOT ILIKE 'pattern'
20456        if matches!(
20457            self.config.dialect,
20458            Some(crate::dialects::DialectType::ClickHouse)
20459        ) && self.check(TokenType::Not)
20460        {
20461            if self.current + 1 < self.tokens.len()
20462                && matches!(
20463                    self.tokens[self.current + 1].token_type,
20464                    TokenType::Like | TokenType::ILike
20465                )
20466            {
20467                self.skip(); // consume NOT
20468                self.skip(); // consume LIKE/ILIKE
20469                let _ = self.parse_primary()?; // consume pattern
20470            }
20471        }
20472
20473        // ClickHouse: SHOW ... ILIKE 'pattern'
20474        if matches!(
20475            self.config.dialect,
20476            Some(crate::dialects::DialectType::ClickHouse)
20477        ) && self.match_token(TokenType::ILike)
20478        {
20479            let _ = self.parse_primary()?; // consume pattern
20480        }
20481
20482        // Check for WHERE clause (MySQL: SHOW STATUS WHERE condition)
20483        let where_clause = if self.match_token(TokenType::Where) {
20484            Some(self.parse_expression()?)
20485        } else {
20486            None
20487        };
20488
20489        // Check for WITH PRIVILEGES clause (Snowflake: SHOW ... WITH PRIVILEGES USAGE, MODIFY)
20490        let privileges = if self.match_token(TokenType::With) && self.match_keyword("PRIVILEGES") {
20491            // Parse comma-separated list of privilege names (no parentheses)
20492            let mut privs = Vec::new();
20493            loop {
20494                if self.is_at_end() || self.check(TokenType::Semicolon) {
20495                    break;
20496                }
20497                let tok = self.peek();
20498                if tok.token_type == TokenType::Var || tok.token_type.is_keyword() {
20499                    privs.push(self.advance().text.to_ascii_uppercase());
20500                    // Check for comma to continue
20501                    if !self.match_token(TokenType::Comma) {
20502                        break;
20503                    }
20504                } else {
20505                    break;
20506                }
20507            }
20508            privs
20509        } else {
20510            Vec::new()
20511        };
20512
20513        // ClickHouse: SHOW ... SETTINGS key=val, key=val
20514        if matches!(
20515            self.config.dialect,
20516            Some(crate::dialects::DialectType::ClickHouse)
20517        ) {
20518            self.parse_clickhouse_settings_clause()?;
20519        }
20520
20521        Ok(Expression::Show(Box::new(Show {
20522            this,
20523            terse,
20524            history,
20525            like,
20526            scope_kind,
20527            scope,
20528            starts_with,
20529            limit,
20530            from,
20531            where_clause,
20532            for_target,
20533            db,
20534            target,
20535            mutex,
20536            privileges,
20537        })))
20538    }
20539
20540    /// Parse COPY statement (Snowflake, PostgreSQL)
20541    /// COPY INTO <table> FROM <source> [(<parameters>)]
20542    /// COPY INTO <location> FROM <table> [(<parameters>)]
20543    fn parse_copy(&mut self) -> Result<Expression> {
20544        self.expect(TokenType::Copy)?;
20545
20546        // Check for INTO (Snowflake/TSQL style: COPY INTO)
20547        let is_into = self.match_token(TokenType::Into);
20548
20549        // Parse target table or location (possibly with column list)
20550        let this = if self.check(TokenType::LParen) {
20551            // Subquery: COPY (SELECT ...) TO ...
20552            self.parse_primary()?
20553        } else if self.check(TokenType::DAt)
20554            || self.check(TokenType::String)
20555            || self.is_stage_reference()
20556        {
20557            // Stage or file destination (for exports): COPY INTO @stage or COPY INTO 's3://...'
20558            self.parse_file_location()?
20559        } else {
20560            // Table reference, possibly with column list: COPY table (col1, col2)
20561            let table = self.parse_table_ref()?;
20562            // Check for column list
20563            if self.check(TokenType::LParen) {
20564                // Peek ahead to see if this is a column list or a subquery
20565                // Column list won't start with SELECT
20566                let has_column_list = {
20567                    let start = self.current;
20568                    self.skip(); // consume (
20569                    let is_select = self.check(TokenType::Select);
20570                    self.current = start; // backtrack
20571                    !is_select
20572                };
20573                if has_column_list {
20574                    self.skip(); // consume (
20575                    let mut columns = Vec::new();
20576                    loop {
20577                        let col_name = self.expect_identifier_or_keyword()?;
20578                        columns.push(col_name);
20579                        if !self.match_token(TokenType::Comma) {
20580                            break;
20581                        }
20582                    }
20583                    self.expect(TokenType::RParen)?;
20584                    // Create a schema expression with the table and columns
20585                    Expression::Schema(Box::new(Schema {
20586                        this: Some(Box::new(Expression::Table(Box::new(table)))),
20587                        expressions: columns
20588                            .into_iter()
20589                            .map(|c| {
20590                                Expression::boxed_column(Column {
20591                                    name: Identifier::new(c),
20592                                    table: None,
20593                                    join_mark: false,
20594                                    trailing_comments: Vec::new(),
20595                                    span: None,
20596                                    inferred_type: None,
20597                                })
20598                            })
20599                            .collect(),
20600                    }))
20601                } else {
20602                    Expression::Table(Box::new(table))
20603                }
20604            } else {
20605                Expression::Table(Box::new(table))
20606            }
20607        };
20608
20609        // Determine direction: FROM means loading into table, TO means exporting
20610        let kind = self.match_token(TokenType::From);
20611        let has_to = if !kind {
20612            // Try TO keyword for export (TO is a keyword token, not an identifier)
20613            self.match_token(TokenType::To)
20614        } else {
20615            false
20616        };
20617
20618        // Parse source/destination files or stage only if FROM/TO was found
20619        // and we're not at a parameter (which would start with identifier = ...)
20620        let mut files = Vec::new();
20621        if kind
20622            || has_to
20623            || self.check(TokenType::String)
20624            || self.is_stage_reference()
20625            || self.check(TokenType::LParen)
20626        {
20627            // Check for subquery: FROM (SELECT ...)
20628            if self.check(TokenType::LParen) {
20629                // Peek ahead to see if this is a subquery
20630                let start = self.current;
20631                self.skip(); // consume (
20632                let is_select = self.check(TokenType::Select);
20633                self.current = start; // backtrack
20634                if is_select {
20635                    // Parse the subquery
20636                    let subquery = self.parse_primary()?;
20637                    files.push(subquery);
20638                }
20639            }
20640            // Parse file location(s) until we hit a parameter or end
20641            while !self.is_at_end() && !self.check(TokenType::Semicolon) && files.is_empty()
20642                || (self.check(TokenType::Comma) && !files.is_empty())
20643            {
20644                // Consume comma if present (for multiple files)
20645                if !files.is_empty() && !self.match_token(TokenType::Comma) {
20646                    break;
20647                }
20648                // Check if this looks like a parameter (identifier followed by =)
20649                // But stage references (@stage) are not parameters
20650                if (self.check(TokenType::Var) || self.check_keyword())
20651                    && !self.is_stage_reference()
20652                {
20653                    let lookahead = self.current + 1;
20654                    if lookahead < self.tokens.len()
20655                        && self.tokens[lookahead].token_type == TokenType::Eq
20656                    {
20657                        break; // This is a parameter, stop parsing files
20658                    }
20659                }
20660                // Check for WITH keyword - stop parsing files
20661                if self.check(TokenType::With) {
20662                    break;
20663                }
20664                // Stop if we don't see a file location start
20665                // Include QuotedIdentifier for Databricks backtick-quoted paths like `s3://link`
20666                if !self.check(TokenType::String)
20667                    && !self.is_stage_reference()
20668                    && !self.check(TokenType::Var)
20669                    && !self.check_keyword()
20670                    && !self.check(TokenType::QuotedIdentifier)
20671                {
20672                    break;
20673                }
20674                // For COPY INTO ... FROM table_name, handle dotted table references
20675                // If the next token is a Var/Identifier and the one after is a Dot, parse as table reference
20676                if (self.check(TokenType::Var) || self.is_identifier_token())
20677                    && !self.is_stage_reference()
20678                {
20679                    let lookahead = self.current + 1;
20680                    let has_dot = lookahead < self.tokens.len()
20681                        && self.tokens[lookahead].token_type == TokenType::Dot;
20682                    if has_dot {
20683                        let table = self.parse_table_ref()?;
20684                        files.push(Expression::Table(Box::new(table)));
20685                        continue;
20686                    }
20687                }
20688                let location = self.parse_file_location()?;
20689                files.push(location);
20690            }
20691        }
20692
20693        // Parse credentials and parameters
20694        let mut params = Vec::new();
20695        let mut credentials = None;
20696        let mut with_wrapped = false;
20697
20698        // Parse Snowflake-style parameters: KEY = VALUE or KEY = (nested values)
20699        // or DuckDB/PostgreSQL WITH (KEY VALUE, ...) format
20700        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
20701            // Match WITH keyword if present (some dialects use WITH before params)
20702            let had_with = self.match_token(TokenType::With);
20703
20704            // Check for wrapped parameters in parentheses
20705            if self.match_token(TokenType::LParen) {
20706                if had_with {
20707                    with_wrapped = true;
20708                }
20709                while !self.check(TokenType::RParen) && !self.is_at_end() {
20710                    let param = self.parse_copy_parameter()?;
20711                    params.push(param);
20712                    // Consume optional comma between params
20713                    self.match_token(TokenType::Comma);
20714                }
20715                self.expect(TokenType::RParen)?;
20716                break;
20717            }
20718
20719            // Parse individual parameter: NAME = value
20720            if self.check(TokenType::Var) || self.check_keyword() {
20721                let param = self.parse_copy_parameter()?;
20722
20723                // Handle special CREDENTIALS parameter (case-insensitive)
20724                if param.name.eq_ignore_ascii_case("CREDENTIALS") {
20725                    // For Redshift-style CREDENTIALS 'string' (single string value)
20726                    // vs Snowflake-style CREDENTIALS = (KEY='value', KEY2='value')
20727                    if let Some(Expression::Literal(lit)) = &param.value {
20728                        if let Literal::String(s) = lit.as_ref() {
20729                            // Redshift style: store as a simple credentials string
20730                            let creds = Credentials {
20731                                credentials: vec![("".to_string(), s.clone())],
20732                                storage: None,
20733                                encryption: None,
20734                            };
20735                            credentials = Some(Box::new(creds));
20736                        }
20737                    } else {
20738                        // Snowflake style: key=value pairs
20739                        let creds = Credentials {
20740                            credentials: param
20741                                .values
20742                                .iter()
20743                                .filter_map(|v| {
20744                                    if let Expression::Eq(eq) = v {
20745                                        let key = if let Expression::Column(c) = &eq.left {
20746                                            c.name.name.clone()
20747                                        } else {
20748                                            return None;
20749                                        };
20750                                        let val = if let Expression::Literal(lit) = &eq.right {
20751                                            if let Literal::String(s) = lit.as_ref() {
20752                                                s.clone()
20753                                            } else {
20754                                                String::new()
20755                                            }
20756                                        } else {
20757                                            return None;
20758                                        };
20759                                        Some((key, val))
20760                                    } else {
20761                                        None
20762                                    }
20763                                })
20764                                .collect(),
20765                            storage: None,
20766                            encryption: None,
20767                        };
20768                        credentials = Some(Box::new(creds));
20769                    }
20770                } else if param.name.eq_ignore_ascii_case("STORAGE_INTEGRATION") {
20771                    // Store STORAGE_INTEGRATION as a regular parameter only
20772                    // Don't use the credentials.storage field for this
20773                    params.push(param);
20774                } else {
20775                    params.push(param);
20776                }
20777            } else {
20778                break;
20779            }
20780        }
20781
20782        Ok(Expression::Copy(Box::new(CopyStmt {
20783            this,
20784            kind,
20785            files,
20786            params,
20787            credentials,
20788            is_into,
20789            with_wrapped,
20790        })))
20791    }
20792
20793    /// Parse a single COPY parameter: NAME = value, NAME = (nested values), or NAME value (no =)
20794    fn parse_copy_parameter(&mut self) -> Result<CopyParameter> {
20795        // Preserve original case for parameter name (important for Redshift COPY options)
20796        let name = self.expect_identifier_or_keyword()?;
20797
20798        let mut value = None;
20799        let mut values = Vec::new();
20800
20801        let has_eq = self.match_token(TokenType::Eq);
20802
20803        if has_eq {
20804            if self.match_token(TokenType::LParen) {
20805                // Nested parameter list: KEY = (nested_key=value, ...) or KEY = (value1, value2)
20806                // Check if this is a list of simple values (like strings) or key=value pairs
20807                // If the first token is a string/number, it's a list of values
20808                if self.check(TokenType::String) || self.check(TokenType::Number) {
20809                    // Simple value list: FILES = ('test1.csv', 'test2.csv')
20810                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20811                        values.push(self.parse_primary()?);
20812                        if !self.match_token(TokenType::Comma) {
20813                            break;
20814                        }
20815                    }
20816                } else {
20817                    // Key=value pairs: CREDENTIALS = (AWS_KEY_ID='id' AWS_SECRET_KEY='key')
20818                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20819                        // Parse nested key=value pairs
20820                        let nested_key = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
20821                        if self.match_token(TokenType::Eq) {
20822                            let nested_value = self.parse_copy_param_value()?;
20823                            // Create an Eq expression for the nested key=value
20824                            values.push(Expression::Eq(Box::new(BinaryOp {
20825                                left: Expression::boxed_column(Column {
20826                                    name: Identifier::new(nested_key),
20827                                    table: None,
20828                                    join_mark: false,
20829                                    trailing_comments: Vec::new(),
20830                                    span: None,
20831                                    inferred_type: None,
20832                                }),
20833                                right: nested_value,
20834                                left_comments: Vec::new(),
20835                                operator_comments: Vec::new(),
20836                                trailing_comments: Vec::new(),
20837                                inferred_type: None,
20838                            })));
20839                        } else {
20840                            // Just a keyword/value without =
20841                            values.push(Expression::boxed_column(Column {
20842                                name: Identifier::new(nested_key),
20843                                table: None,
20844                                join_mark: false,
20845                                trailing_comments: Vec::new(),
20846                                span: None,
20847                                inferred_type: None,
20848                            }));
20849                        }
20850                        // Consume optional comma between nested values
20851                        self.match_token(TokenType::Comma);
20852                    }
20853                }
20854                self.expect(TokenType::RParen)?;
20855            } else {
20856                // Simple value: KEY = value
20857                value = Some(self.parse_copy_param_value()?);
20858            }
20859        } else {
20860            // No = sign: DuckDB/PostgreSQL format (KEY value or KEY (col1, col2))
20861            // Check if followed by a value: string, number, boolean, identifier, or tuple
20862            if self.check(TokenType::LParen) {
20863                // Check if this is a COPY_INTO_VARLEN_OPTIONS parameter
20864                // These are Databricks/Snowflake options that contain key='value' pairs without = before (
20865                let is_varlen_option = matches!(
20866                    name.as_str(),
20867                    "FORMAT_OPTIONS" | "COPY_OPTIONS" | "FILE_FORMAT" | "CREDENTIAL"
20868                );
20869
20870                self.skip(); // consume (
20871
20872                if is_varlen_option {
20873                    // Parse as key='value' pairs: FORMAT_OPTIONS ('opt1'='true', 'opt2'='test')
20874                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20875                        if self.check(TokenType::String) {
20876                            // Parse 'key'='value' pair
20877                            let key_token = self.advance();
20878                            let key = key_token.text.clone();
20879                            if self.match_token(TokenType::Eq) {
20880                                let val = self.parse_copy_param_value()?;
20881                                values.push(Expression::Eq(Box::new(BinaryOp {
20882                                    left: Expression::Literal(Box::new(Literal::String(key))),
20883                                    right: val,
20884                                    left_comments: Vec::new(),
20885                                    operator_comments: Vec::new(),
20886                                    trailing_comments: Vec::new(),
20887                                    inferred_type: None,
20888                                })));
20889                            } else {
20890                                // Just a string without =
20891                                values.push(Expression::Literal(Box::new(Literal::String(key))));
20892                            }
20893                        } else if self.check(TokenType::Var)
20894                            || self.check_keyword()
20895                            || self.is_identifier_token()
20896                        {
20897                            // Parse identifier='value' pair (unquoted key)
20898                            let key = self.advance().text.clone();
20899                            if self.match_token(TokenType::Eq) {
20900                                let val = self.parse_copy_param_value()?;
20901                                values.push(Expression::Eq(Box::new(BinaryOp {
20902                                    left: Expression::boxed_column(Column {
20903                                        name: Identifier::new(key),
20904                                        table: None,
20905                                        join_mark: false,
20906                                        trailing_comments: Vec::new(),
20907                                        span: None,
20908                                        inferred_type: None,
20909                                    }),
20910                                    right: val,
20911                                    left_comments: Vec::new(),
20912                                    operator_comments: Vec::new(),
20913                                    trailing_comments: Vec::new(),
20914                                    inferred_type: None,
20915                                })));
20916                            } else {
20917                                // Just an identifier without =
20918                                values.push(Expression::boxed_column(Column {
20919                                    name: Identifier::new(key),
20920                                    table: None,
20921                                    join_mark: false,
20922                                    trailing_comments: Vec::new(),
20923                                    span: None,
20924                                    inferred_type: None,
20925                                }));
20926                            }
20927                        } else {
20928                            break;
20929                        }
20930                        self.match_token(TokenType::Comma);
20931                    }
20932                } else {
20933                    // Tuple value: FORCE_NOT_NULL (col1, col2)
20934                    let mut items = Vec::new();
20935                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20936                        items.push(self.parse_primary()?);
20937                        if !self.match_token(TokenType::Comma) {
20938                            break;
20939                        }
20940                    }
20941                    value = Some(Expression::Tuple(Box::new(Tuple { expressions: items })));
20942                }
20943                self.expect(TokenType::RParen)?;
20944            } else if self.check(TokenType::LBrace) {
20945                // Map literal: KV_METADATA {'key': 'value', ...}
20946                value = Some(self.parse_primary()?);
20947            } else if self.check(TokenType::String) || self.check(TokenType::Number) {
20948                // String or number value
20949                value = Some(self.parse_copy_param_value()?);
20950            } else if self.check(TokenType::True) || self.check(TokenType::False) {
20951                // Boolean value (TRUE/FALSE are keyword tokens)
20952                value = Some(self.parse_copy_param_value()?);
20953            } else if !self.check(TokenType::Comma)
20954                && !self.check(TokenType::RParen)
20955                && !self.is_at_end()
20956                && !self.check(TokenType::Semicolon)
20957            {
20958                // Identifier value: FORMAT JSON, HEADER MATCH, etc.
20959                // But skip if this is a known flag-only parameter (Redshift COPY options that take no value)
20960                let name_upper = name.to_ascii_uppercase();
20961                let is_flag_param = matches!(
20962                    name_upper.as_str(),
20963                    "EMPTYASNULL"
20964                        | "BLANKSASNULL"
20965                        | "ACCEPTINVCHARS"
20966                        | "COMPUPDATE"
20967                        | "STATUPDATE"
20968                        | "NOLOAD"
20969                        | "ESCAPE"
20970                        | "REMOVEQUOTES"
20971                        | "EXPLICIT_IDS"
20972                        | "FILLRECORD"
20973                        | "TRIMBLANKS"
20974                        | "TRUNCATECOLUMNS"
20975                        | "ROUNDEC"
20976                        | "IGNOREHEADER"
20977                        | "IGNOREBLANKLINES"
20978                        | "ACCEPTANYDATE"
20979                );
20980                if !is_flag_param && (self.check(TokenType::Var) || self.check_keyword()) {
20981                    value = Some(self.parse_copy_param_value()?);
20982                }
20983            }
20984            // If nothing matched, it's a bare flag parameter with no value (allowed)
20985        }
20986
20987        Ok(CopyParameter {
20988            name,
20989            value,
20990            values,
20991            eq: has_eq,
20992        })
20993    }
20994
20995    /// Parse a value for COPY parameters (handles strings, identifiers, numbers, lists)
20996    fn parse_copy_param_value(&mut self) -> Result<Expression> {
20997        // Handle lists like ('file1', 'file2')
20998        if self.match_token(TokenType::LParen) {
20999            let mut items = Vec::new();
21000            while !self.check(TokenType::RParen) && !self.is_at_end() {
21001                items.push(self.parse_primary()?);
21002                if !self.match_token(TokenType::Comma) {
21003                    break;
21004                }
21005            }
21006            self.expect(TokenType::RParen)?;
21007            return Ok(Expression::Tuple(Box::new(Tuple { expressions: items })));
21008        }
21009
21010        // Handle string, number, boolean, identifier
21011        if self.check(TokenType::String) {
21012            let token = self.advance();
21013            return Ok(Expression::Literal(Box::new(Literal::String(
21014                token.text.clone(),
21015            ))));
21016        }
21017        // Handle quoted identifier (e.g., STORAGE_INTEGRATION = "storage")
21018        if self.check(TokenType::QuotedIdentifier) {
21019            let token = self.advance();
21020            return Ok(Expression::boxed_column(Column {
21021                name: Identifier::quoted(token.text.clone()),
21022                table: None,
21023                join_mark: false,
21024                trailing_comments: Vec::new(),
21025                span: None,
21026                inferred_type: None,
21027            }));
21028        }
21029        if self.check(TokenType::Number) {
21030            let token = self.advance();
21031            return Ok(Expression::Literal(Box::new(Literal::Number(
21032                token.text.clone(),
21033            ))));
21034        }
21035        if self.match_token(TokenType::True) {
21036            return Ok(Expression::Boolean(BooleanLiteral { value: true }));
21037        }
21038        if self.match_token(TokenType::False) {
21039            return Ok(Expression::Boolean(BooleanLiteral { value: false }));
21040        }
21041        // Identifier (e.g., FORMAT_NAME=my_format)
21042        if self.check(TokenType::Var) || self.check_keyword() {
21043            // Could be a qualified name like MY_DATABASE.MY_SCHEMA.MY_FORMAT
21044            let first = self.advance().text.clone();
21045            if self.match_token(TokenType::Dot) {
21046                let second = self.expect_identifier_or_keyword()?;
21047                if self.match_token(TokenType::Dot) {
21048                    let third = self.expect_identifier_or_keyword()?;
21049                    return Ok(Expression::boxed_column(Column {
21050                        name: Identifier::new(format!("{}.{}.{}", first, second, third)),
21051                        table: None,
21052                        join_mark: false,
21053                        trailing_comments: Vec::new(),
21054                        span: None,
21055                        inferred_type: None,
21056                    }));
21057                }
21058                return Ok(Expression::boxed_column(Column {
21059                    name: Identifier::new(format!("{}.{}", first, second)),
21060                    table: None,
21061                    join_mark: false,
21062                    trailing_comments: Vec::new(),
21063                    span: None,
21064                    inferred_type: None,
21065                }));
21066            }
21067            return Ok(Expression::boxed_column(Column {
21068                name: Identifier::new(first),
21069                table: None,
21070                join_mark: false,
21071                trailing_comments: Vec::new(),
21072                span: None,
21073                inferred_type: None,
21074            }));
21075        }
21076
21077        Err(self.parse_error("Expected value for COPY parameter"))
21078    }
21079
21080    /// Parse Snowflake stage reference when tokenized as String (e.g., '@mystage', '@external/location')
21081    /// Handles: '@mystage', '@external/location'
21082    fn parse_stage_reference_from_string(&mut self) -> Result<Expression> {
21083        use crate::expressions::StageReference;
21084
21085        // The String token contains @ and the entire path
21086        let string_token = self.advance();
21087        let full_path = string_token.text.clone();
21088
21089        // Split on / to get stage name and path
21090        let parts: Vec<&str> = full_path.splitn(2, '/').collect();
21091        let name = parts[0].to_string();
21092        let path = if parts.len() > 1 {
21093            Some(format!("/{}", parts[1]))
21094        } else {
21095            None
21096        };
21097
21098        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
21099        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
21100            let mut ff = None;
21101            let mut pat = None;
21102
21103            loop {
21104                if self.match_identifier("FILE_FORMAT") {
21105                    self.expect(TokenType::FArrow)?; // =>
21106                    ff = Some(self.parse_primary()?);
21107                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
21108                    // PATTERN can be tokenized as keyword or identifier
21109                    self.expect(TokenType::FArrow)?; // =>
21110                    if let Expression::Literal(lit) = self.parse_primary()? {
21111                        if let Literal::String(s) = lit.as_ref() {
21112                            pat = Some(s.clone());
21113                        }
21114                    }
21115                } else {
21116                    break;
21117                }
21118
21119                if !self.match_token(TokenType::Comma) {
21120                    break;
21121                }
21122            }
21123
21124            self.expect(TokenType::RParen)?;
21125            (ff, pat)
21126        } else {
21127            (None, None)
21128        };
21129
21130        Ok(Expression::StageReference(Box::new(StageReference {
21131            name,
21132            path,
21133            file_format,
21134            pattern,
21135            quoted: true, // Stage reference came from a quoted string
21136        })))
21137    }
21138
21139    /// Parse Snowflake stage reference when tokenized as Var (e.g., @mystage becomes Var token)
21140    /// Handles: @mystage, @mystage/path/to/file.csv
21141    fn parse_stage_reference_from_var(&mut self) -> Result<Expression> {
21142        use crate::expressions::StageReference;
21143
21144        // The Var token already contains @ and the stage name
21145        let var_token = self.advance();
21146        let mut name = var_token.text.clone();
21147
21148        // Handle qualified names: @namespace.stage
21149        while self.match_token(TokenType::Dot) {
21150            name.push('.');
21151            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
21152                name.push_str(&self.advance().text);
21153            } else if self.check(TokenType::Percent) {
21154                // Handle table stage in qualified path: @namespace.%table_name
21155                self.skip();
21156                name.push('%');
21157                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
21158                    name.push_str(&self.advance().text);
21159                }
21160            } else {
21161                break;
21162            }
21163        }
21164
21165        // Handle path after stage: @stage/path/to/file.csv
21166        let path = if self.match_token(TokenType::Slash) {
21167            let mut path_str = String::from("/");
21168            // Consume path components until we hit whitespace/paren/etc.
21169            while !self.is_at_end() {
21170                if self.check(TokenType::Identifier)
21171                    || self.check(TokenType::Var)
21172                    || self.check(TokenType::Number)
21173                    || self.check(TokenType::Dot)
21174                    || self.check(TokenType::Dash)
21175                    || self.check(TokenType::Star)
21176                    || self.check(TokenType::To)
21177                    || self.is_safe_keyword_as_identifier()
21178                {
21179                    path_str.push_str(&self.advance().text);
21180                } else if self.match_token(TokenType::Slash) {
21181                    path_str.push('/');
21182                } else {
21183                    break;
21184                }
21185            }
21186            Some(path_str)
21187        } else {
21188            None
21189        };
21190
21191        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
21192        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
21193            let mut ff = None;
21194            let mut pat = None;
21195
21196            loop {
21197                if self.match_identifier("FILE_FORMAT") {
21198                    self.expect(TokenType::FArrow)?; // =>
21199                    ff = Some(self.parse_primary()?);
21200                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
21201                    // PATTERN can be tokenized as keyword or identifier
21202                    self.expect(TokenType::FArrow)?; // =>
21203                    if let Expression::Literal(lit) = self.parse_primary()? {
21204                        if let Literal::String(s) = lit.as_ref() {
21205                            pat = Some(s.clone());
21206                        }
21207                    }
21208                } else {
21209                    break;
21210                }
21211
21212                if !self.match_token(TokenType::Comma) {
21213                    break;
21214                }
21215            }
21216
21217            self.expect(TokenType::RParen)?;
21218            (ff, pat)
21219        } else {
21220            (None, None)
21221        };
21222
21223        Ok(Expression::StageReference(Box::new(StageReference {
21224            name,
21225            path,
21226            file_format,
21227            pattern,
21228            quoted: false,
21229        })))
21230    }
21231
21232    /// Parse Snowflake stage reference in FROM clause
21233    /// Handles: @stage, @"stage", @namespace.stage, @stage/path/file.csv, @~, @%table
21234    fn parse_stage_reference(&mut self) -> Result<Expression> {
21235        use crate::expressions::StageReference;
21236
21237        self.expect(TokenType::DAt)?; // consume @
21238
21239        // Build the stage name - can include dots, slashes, etc.
21240        let mut name = String::from("@");
21241
21242        // Handle special stage types:
21243        // @~ = user stage
21244        // @% = table stage (followed by table name)
21245        if self.check(TokenType::Tilde) {
21246            self.skip();
21247            name.push('~');
21248        } else if self.check(TokenType::Percent) {
21249            self.skip();
21250            name.push('%');
21251            // Table name follows (can be qualified: schema.table)
21252            loop {
21253                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
21254                    name.push_str(&self.advance().text);
21255                } else {
21256                    break;
21257                }
21258                // Handle qualified table names: %db.schema.table
21259                if self.match_token(TokenType::Dot) {
21260                    name.push('.');
21261                } else {
21262                    break;
21263                }
21264            }
21265        } else {
21266            // Handle quoted or unquoted stage names
21267            loop {
21268                if self.check(TokenType::QuotedIdentifier) {
21269                    // Preserve quotes for quoted identifiers
21270                    let text = self.advance().text;
21271                    name.push('"');
21272                    name.push_str(&text);
21273                    name.push('"');
21274                } else if self.check(TokenType::Percent) {
21275                    // Handle table stage in qualified path: @namespace.%table_name
21276                    self.skip();
21277                    name.push('%');
21278                    if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
21279                        name.push_str(&self.advance().text);
21280                    }
21281                } else if self.check(TokenType::Identifier)
21282                    || self.check(TokenType::Var)
21283                    || self.is_safe_keyword_as_identifier()
21284                {
21285                    name.push_str(&self.advance().text);
21286                } else {
21287                    break;
21288                }
21289
21290                // Handle dots for qualified names: @namespace.stage or @"schema"."stage"
21291                if self.match_token(TokenType::Dot) {
21292                    name.push('.');
21293                } else {
21294                    break;
21295                }
21296            }
21297        }
21298
21299        // Handle path after stage: @stage/path/to/file.csv
21300        let path = if self.match_token(TokenType::Slash) {
21301            let mut path_str = String::from("/");
21302            // Consume path components until we hit whitespace/paren/etc.
21303            // Note: path can include keywords like 'to', 'data', etc.
21304            while !self.is_at_end() {
21305                if self.check(TokenType::Identifier)
21306                    || self.check(TokenType::Var)
21307                    || self.check(TokenType::Number)
21308                    || self.check(TokenType::Dot)
21309                    || self.check(TokenType::Dash)
21310                    || self.check(TokenType::Star)
21311                    || self.check(TokenType::To)
21312                    || self.is_safe_keyword_as_identifier()
21313                {
21314                    path_str.push_str(&self.advance().text);
21315                } else if self.match_token(TokenType::Slash) {
21316                    path_str.push('/');
21317                } else {
21318                    break;
21319                }
21320            }
21321            Some(path_str)
21322        } else {
21323            None
21324        };
21325
21326        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
21327        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
21328            let mut ff = None;
21329            let mut pat = None;
21330
21331            loop {
21332                if self.match_identifier("FILE_FORMAT") {
21333                    self.expect(TokenType::FArrow)?; // =>
21334                    ff = Some(self.parse_primary()?);
21335                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
21336                    // PATTERN can be tokenized as keyword or identifier
21337                    self.expect(TokenType::FArrow)?; // =>
21338                    if let Expression::Literal(lit) = self.parse_primary()? {
21339                        if let Literal::String(s) = lit.as_ref() {
21340                            pat = Some(s.clone());
21341                        }
21342                    }
21343                } else {
21344                    break;
21345                }
21346
21347                if !self.match_token(TokenType::Comma) {
21348                    break;
21349                }
21350            }
21351
21352            self.expect(TokenType::RParen)?;
21353            (ff, pat)
21354        } else {
21355            (None, None)
21356        };
21357
21358        Ok(Expression::StageReference(Box::new(StageReference {
21359            name,
21360            path,
21361            file_format,
21362            pattern,
21363            quoted: false,
21364        })))
21365    }
21366
21367    /// Parse file location for COPY/PUT statements
21368    /// Handles: @stage, @db.schema.stage, @stage/path, 's3://bucket/path', file:///path
21369    fn parse_file_location(&mut self) -> Result<Expression> {
21370        // Stage reference starting with @ (tokenized as DAt or as a Var starting with @)
21371        if self.check(TokenType::DAt) {
21372            self.skip(); // consume @
21373            let mut stage_path = String::from("@");
21374
21375            // Handle table stage prefix: @%table
21376            if self.check(TokenType::Percent) || self.check(TokenType::Mod) {
21377                stage_path.push('%');
21378                self.skip(); // consume %
21379            }
21380            // Handle user stage: @~
21381            else if self.check(TokenType::Tilde) {
21382                stage_path.push('~');
21383                self.skip(); // consume ~
21384            }
21385
21386            // Get stage name
21387            if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token() {
21388                stage_path.push_str(&self.advance().text);
21389            }
21390            // Parse qualified name parts: .schema.stage
21391            while self.check(TokenType::Dot) {
21392                self.skip(); // consume .
21393                stage_path.push('.');
21394                if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token()
21395                {
21396                    stage_path.push_str(&self.advance().text);
21397                }
21398            }
21399            // Parse path after stage: /path/to/file.csv
21400            // Consume all connected path components (dots, dashes, numbers, etc.)
21401            // matching the logic in parse_stage_reference.
21402            if self.match_token(TokenType::Slash) {
21403                stage_path.push('/');
21404                while !self.is_at_end() {
21405                    if (self.check(TokenType::Var)
21406                        || self.check(TokenType::Identifier)
21407                        || self.check(TokenType::Number)
21408                        || self.check(TokenType::Dot)
21409                        || self.check(TokenType::Dash)
21410                        || self.check(TokenType::Star)
21411                        || self.check(TokenType::To)
21412                        || self.is_safe_keyword_as_identifier())
21413                        && !self.check_next(TokenType::Eq)
21414                    {
21415                        stage_path.push_str(&self.advance().text);
21416                    } else if self.match_token(TokenType::Slash) {
21417                        stage_path.push('/');
21418                    } else {
21419                        break;
21420                    }
21421                }
21422            }
21423            return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21424        }
21425
21426        // Stage reference tokenized as a Var starting with @ (e.g., @random_stage)
21427        // This happens when the tokenizer combines @ with the following identifier
21428        if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
21429            let mut stage_path = self.advance().text.clone();
21430            // Parse qualified name parts: .schema.stage
21431            while self.check(TokenType::Dot) {
21432                self.skip(); // consume .
21433                stage_path.push('.');
21434                if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token()
21435                {
21436                    stage_path.push_str(&self.advance().text);
21437                }
21438            }
21439            // Parse path after stage: /path/to/file.csv
21440            if self.match_token(TokenType::Slash) {
21441                stage_path.push('/');
21442                while !self.is_at_end() {
21443                    if (self.check(TokenType::Var)
21444                        || self.check(TokenType::Identifier)
21445                        || self.check(TokenType::Number)
21446                        || self.check(TokenType::Dot)
21447                        || self.check(TokenType::Dash)
21448                        || self.check(TokenType::Star)
21449                        || self.check(TokenType::To)
21450                        || self.is_safe_keyword_as_identifier())
21451                        && !self.check_next(TokenType::Eq)
21452                    {
21453                        stage_path.push_str(&self.advance().text);
21454                    } else if self.match_token(TokenType::Slash) {
21455                        stage_path.push('/');
21456                    } else {
21457                        break;
21458                    }
21459                }
21460            }
21461            return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21462        }
21463
21464        // String literal (file path or URL)
21465        if self.check(TokenType::String) {
21466            let token = self.advance();
21467            return Ok(Expression::Literal(Box::new(Literal::String(
21468                token.text.clone(),
21469            ))));
21470        }
21471
21472        // Backtick-quoted identifier (Databricks style: `s3://link`)
21473        if self.check(TokenType::QuotedIdentifier) {
21474            let token = self.advance();
21475            return Ok(Expression::Identifier(Identifier::quoted(
21476                token.text.clone(),
21477            )));
21478        }
21479
21480        // Identifier (could be a stage name without @)
21481        if self.check(TokenType::Var) || self.check_keyword() {
21482            let ident = self.advance().text.clone();
21483            return Ok(Expression::boxed_column(Column {
21484                name: Identifier::new(ident),
21485                table: None,
21486                join_mark: false,
21487                trailing_comments: Vec::new(),
21488                span: None,
21489                inferred_type: None,
21490            }));
21491        }
21492
21493        Err(self.parse_error("Expected file location"))
21494    }
21495
21496    /// Parse Snowflake stage reference as a string for PUT/GET/COPY statements
21497    /// Handles: @stage, @%table, @~, @db.schema.stage, @"quoted"."stage", @stage/path
21498    /// Returns a Literal::String containing the stage path
21499    fn parse_stage_reference_as_string(&mut self) -> Result<Expression> {
21500        // Stage reference starting with @ (tokenized as DAt)
21501        if self.check(TokenType::DAt) {
21502            self.skip(); // consume @
21503            let mut stage_path = String::from("@");
21504
21505            // Handle table stage prefix: @%table
21506            if self.check(TokenType::Percent) || self.check(TokenType::Mod) {
21507                stage_path.push('%');
21508                self.skip(); // consume %
21509            }
21510            // Handle user stage: @~
21511            else if self.check(TokenType::Tilde) {
21512                stage_path.push('~');
21513                self.skip(); // consume ~
21514                             // After @~, parse any path segments
21515                while self.check(TokenType::Slash) {
21516                    self.skip(); // consume /
21517                    stage_path.push('/');
21518                    if (self.check(TokenType::Var)
21519                        || self.check_keyword()
21520                        || self.is_identifier_token())
21521                        && !self.check_next(TokenType::Eq)
21522                    {
21523                        stage_path.push_str(&self.advance().text);
21524                    }
21525                }
21526                return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21527            }
21528
21529            // Get stage name (could be quoted identifier)
21530            if self.check(TokenType::QuotedIdentifier) {
21531                // Preserve quoted identifier with quotes
21532                let text = &self.peek().text;
21533                stage_path.push('"');
21534                stage_path.push_str(text);
21535                stage_path.push('"');
21536                self.skip();
21537            } else if self.check(TokenType::Var)
21538                || self.check_keyword()
21539                || self.check(TokenType::Identifier)
21540            {
21541                stage_path.push_str(&self.advance().text);
21542            }
21543
21544            // Parse qualified name parts: .schema.stage (may include quoted identifiers)
21545            while self.check(TokenType::Dot) {
21546                self.skip(); // consume .
21547                stage_path.push('.');
21548                if self.check(TokenType::QuotedIdentifier) {
21549                    // Preserve quoted identifier with quotes
21550                    let text = &self.peek().text;
21551                    stage_path.push('"');
21552                    stage_path.push_str(text);
21553                    stage_path.push('"');
21554                    self.skip();
21555                } else if self.check(TokenType::Var)
21556                    || self.check_keyword()
21557                    || self.check(TokenType::Identifier)
21558                {
21559                    stage_path.push_str(&self.advance().text);
21560                }
21561            }
21562
21563            // Parse path segments: /path/to/file
21564            while self.check(TokenType::Slash) {
21565                self.skip(); // consume /
21566                stage_path.push('/');
21567                // Get path segment but don't consume if followed by = (that's a parameter)
21568                if (self.check(TokenType::Var)
21569                    || self.check_keyword()
21570                    || self.is_identifier_token())
21571                    && !self.check_next(TokenType::Eq)
21572                {
21573                    stage_path.push_str(&self.advance().text);
21574                }
21575            }
21576            return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21577        }
21578
21579        // Stage reference tokenized as a Var starting with @ (e.g., @s1)
21580        if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
21581            let mut stage_path = self.advance().text.clone();
21582
21583            // Parse qualified name parts: .schema.stage (may include quoted identifiers)
21584            while self.check(TokenType::Dot) {
21585                self.skip(); // consume .
21586                stage_path.push('.');
21587                if self.check(TokenType::QuotedIdentifier) {
21588                    let text = &self.peek().text;
21589                    stage_path.push('"');
21590                    stage_path.push_str(text);
21591                    stage_path.push('"');
21592                    self.skip();
21593                } else if self.check(TokenType::Var)
21594                    || self.check_keyword()
21595                    || self.check(TokenType::Identifier)
21596                {
21597                    stage_path.push_str(&self.advance().text);
21598                }
21599            }
21600
21601            // Parse path segments: /path/to/file
21602            while self.check(TokenType::Slash) {
21603                self.skip(); // consume /
21604                stage_path.push('/');
21605                if (self.check(TokenType::Var)
21606                    || self.check_keyword()
21607                    || self.is_identifier_token())
21608                    && !self.check_next(TokenType::Eq)
21609                {
21610                    stage_path.push_str(&self.advance().text);
21611                }
21612            }
21613            return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21614        }
21615
21616        Err(self.parse_error("Expected stage reference starting with @"))
21617    }
21618
21619    /// Parse PUT statement (Snowflake)
21620    /// PUT file://<path> @<stage> [AUTO_COMPRESS = TRUE|FALSE] ...
21621    fn parse_put(&mut self) -> Result<Expression> {
21622        self.expect(TokenType::Put)?;
21623
21624        // Parse source file path (usually file:///path/to/file)
21625        let (source, source_quoted) = if self.check(TokenType::String) {
21626            (self.advance().text.clone(), true)
21627        } else {
21628            // Handle file://path syntax (parsed as identifier + colon + etc.)
21629            // Stop when we see @ (start of stage reference)
21630            let mut source_parts = Vec::new();
21631            while !self.is_at_end() {
21632                // Stop if we see @ (DAt token or Var starting with @)
21633                if self.check(TokenType::DAt) {
21634                    break;
21635                }
21636                if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
21637                    break;
21638                }
21639                let token = self.advance();
21640                source_parts.push(token.text.clone());
21641            }
21642            (source_parts.join(""), false)
21643        };
21644
21645        // Parse target stage (@stage_name)
21646        let target = self.parse_stage_reference_as_string()?;
21647
21648        // Parse optional parameters
21649        // Note: Some parameter names like OVERWRITE are keywords, so we check for those explicitly
21650        // Preserve original casing for identity tests
21651        let mut params = Vec::new();
21652        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21653            let is_param_name = self.check(TokenType::Var)
21654                || self.check_keyword()
21655                || self.check(TokenType::Overwrite);
21656            if is_param_name {
21657                let name = self.advance().text.clone();
21658                let value = if self.match_token(TokenType::Eq) {
21659                    Some(self.parse_primary()?)
21660                } else {
21661                    None
21662                };
21663                params.push(CopyParameter {
21664                    name,
21665                    value,
21666                    values: Vec::new(),
21667                    eq: true,
21668                });
21669            } else {
21670                break;
21671            }
21672        }
21673
21674        Ok(Expression::Put(Box::new(PutStmt {
21675            source,
21676            source_quoted,
21677            target,
21678            params,
21679        })))
21680    }
21681
21682    /// Helper to join command tokens with smart spacing
21683    /// Preserves the structure of file paths, stage references, etc.
21684    fn join_command_tokens(&self, tokens: Vec<(String, TokenType)>) -> String {
21685        let mut result = String::new();
21686        let mut prev_token_type: Option<TokenType> = None;
21687        let mut prev_prev_token_type: Option<TokenType> = None;
21688
21689        for (i, (text, token_type)) in tokens.iter().enumerate() {
21690            let needs_space = if result.is_empty() {
21691                false
21692            } else {
21693                match (prev_token_type, *token_type) {
21694                    // No space after @ (stage references: @stage, @%, @~)
21695                    (Some(TokenType::DAt), _) => false,
21696                    // No space around dots (identifiers: a.b.c)
21697                    (Some(TokenType::Dot), _) => false,
21698                    (_, TokenType::Dot) => false,
21699                    // No space around parentheses
21700                    (Some(TokenType::LParen), _) => false,
21701                    (_, TokenType::LParen) => false,
21702                    (_, TokenType::RParen) => false,
21703                    // No space around square brackets (array access: arr[i])
21704                    (Some(TokenType::LBracket), _) => false,
21705                    (_, TokenType::LBracket) => false,
21706                    (_, TokenType::RBracket) => false,
21707                    // No space before ,
21708                    (_, TokenType::Comma) => false,
21709                    // No space around / (paths: @s1/test)
21710                    (Some(TokenType::Slash), _) => false,
21711                    (_, TokenType::Slash) => false,
21712                    // No space around : (file://path)
21713                    (Some(TokenType::Colon), _) => false,
21714                    (_, TokenType::Colon) => false,
21715                    // No space around % (table stage: @%table)
21716                    (Some(TokenType::Mod), _) => false,
21717                    (_, TokenType::Mod) => false,
21718                    (Some(TokenType::Percent), _) => false,
21719                    (_, TokenType::Percent) => false,
21720                    // Handle = contextually:
21721                    // - No space around = in simple KEY=VALUE patterns where value is terminal
21722                    //   (PARALLEL=1, ENABLED=TRUE, FILE_FORMAT='csv')
21723                    // - Keep space for expressions like SET x = x + 1
21724                    (Some(TokenType::Var), TokenType::Eq) => {
21725                        // If the var starts with @ (parameter like @id = 123), always use spaces
21726                        if i >= 1 && tokens[i - 1].0.starts_with('@') {
21727                            true
21728                        } else if i + 1 < tokens.len() {
21729                            // Check what follows: Var=Number where number is terminal (end or followed by Var)
21730                            let next_type = tokens[i + 1].1;
21731                            // Is the value terminal (end of tokens, or followed by another Var=... pattern)?
21732                            let is_terminal_value =
21733                                i + 2 >= tokens.len() || tokens[i + 2].1 == TokenType::Var;
21734                            match next_type {
21735                                // No space for terminal numbers/bools: PARALLEL=1, ENABLED=TRUE
21736                                // Return false (no space) when terminal
21737                                TokenType::Number | TokenType::True | TokenType::False => {
21738                                    !is_terminal_value
21739                                }
21740                                // No space for terminal strings: FILE_FORMAT='csv'
21741                                TokenType::String => !is_terminal_value,
21742                                // Always space if followed by Var (SET x = y ...)
21743                                _ => true,
21744                            }
21745                        } else {
21746                            true
21747                        }
21748                    }
21749                    // No space after = in terminal KEY=VALUE patterns
21750                    (Some(TokenType::Eq), TokenType::Number)
21751                    | (Some(TokenType::Eq), TokenType::True)
21752                    | (Some(TokenType::Eq), TokenType::False)
21753                    | (Some(TokenType::Eq), TokenType::String) => {
21754                        // Is this a terminal value (end or followed by another Var=...)?
21755                        let is_terminal =
21756                            i + 1 >= tokens.len() || tokens[i + 1].1 == TokenType::Var;
21757                        match prev_prev_token_type {
21758                            // No space (return false) when terminal, space otherwise
21759                            // But always space if the var before = was preceded by @ (parameter)
21760                            Some(TokenType::Var) => {
21761                                // Always space if the var before = starts with @ (parameter)
21762                                if i >= 2 && tokens[i - 2].0.starts_with('@') {
21763                                    true
21764                                } else {
21765                                    !is_terminal
21766                                }
21767                            }
21768                            _ => true, // Space for other cases
21769                        }
21770                    }
21771                    // Always space after = when followed by Var (SET x = y, could be expression)
21772                    (Some(TokenType::Eq), TokenType::Var) => true,
21773                    // No space around :: (cast)
21774                    (Some(TokenType::DColon), _) => false,
21775                    (_, TokenType::DColon) => false,
21776                    // Default: add space
21777                    _ => true,
21778                }
21779            };
21780
21781            if needs_space {
21782                result.push(' ');
21783            }
21784            result.push_str(text);
21785            prev_prev_token_type = prev_token_type;
21786            prev_token_type = Some(*token_type);
21787        }
21788        result
21789    }
21790
21791    /// Join Teradata table option tokens with Teradata-specific spacing
21792    /// - No spaces around '='
21793    /// - No spaces around dots or parentheses
21794    /// - Space-separated words otherwise
21795    fn join_teradata_option_tokens(&self, tokens: Vec<(String, TokenType)>) -> String {
21796        let mut result = String::new();
21797        let mut prev_token_type: Option<TokenType> = None;
21798
21799        for (text, token_type) in tokens {
21800            let needs_space = if result.is_empty() {
21801                false
21802            } else {
21803                match (prev_token_type, token_type) {
21804                    (Some(TokenType::Dot), _) => false,
21805                    (_, TokenType::Dot) => false,
21806                    (Some(TokenType::LParen), _) => false,
21807                    (_, TokenType::LParen) => false,
21808                    (_, TokenType::RParen) => false,
21809                    (_, TokenType::Comma) => false,
21810                    (Some(TokenType::Eq), _) => false,
21811                    (_, TokenType::Eq) => false,
21812                    _ => true,
21813                }
21814            };
21815
21816            if needs_space {
21817                result.push(' ');
21818            }
21819            result.push_str(&text);
21820            prev_token_type = Some(token_type);
21821        }
21822
21823        result
21824    }
21825
21826    /// Parse RM or REMOVE command (Snowflake)
21827    /// RM @stage_name / REMOVE @stage_name
21828    fn parse_rm_command(&mut self) -> Result<Expression> {
21829        let command_token = self.advance(); // RM or REMOVE
21830        let command_name = command_token.text.to_ascii_uppercase();
21831
21832        // Collect remaining tokens with their types
21833        let mut tokens = vec![(command_name, command_token.token_type)];
21834        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21835            let token = self.advance();
21836            tokens.push((token.text.clone(), token.token_type));
21837        }
21838
21839        Ok(Expression::Command(Box::new(Command {
21840            this: self.join_command_tokens(tokens),
21841        })))
21842    }
21843
21844    /// Parse GET command (Snowflake)
21845    /// GET @stage_name 'file:///path'
21846    fn parse_get_command(&mut self) -> Result<Expression> {
21847        let get_token = self.advance(); // consume GET (it's already matched)
21848
21849        // Collect remaining tokens with their types, preserving quotes
21850        let mut tokens = vec![("GET".to_string(), get_token.token_type)];
21851        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21852            let token = self.advance();
21853            // Re-add quotes around string and quoted identifier tokens
21854            let text = match token.token_type {
21855                TokenType::String => format!("'{}'", token.text),
21856                TokenType::QuotedIdentifier => format!("\"{}\"", token.text),
21857                _ => token.text.clone(),
21858            };
21859            tokens.push((text, token.token_type));
21860        }
21861
21862        Ok(Expression::Command(Box::new(Command {
21863            this: self.join_command_tokens(tokens),
21864        })))
21865    }
21866
21867    /// Parse CALL statement (stored procedure call)
21868    /// CALL procedure_name(args, ...)
21869    fn parse_call(&mut self) -> Result<Expression> {
21870        let call_token = self.advance(); // consume CALL
21871
21872        // Collect remaining tokens with their types
21873        let mut tokens = vec![("CALL".to_string(), call_token.token_type)];
21874        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21875            let token = self.advance();
21876            tokens.push((token.text.clone(), token.token_type));
21877        }
21878
21879        Ok(Expression::Command(Box::new(Command {
21880            this: self.join_command_tokens(tokens),
21881        })))
21882    }
21883
21884    /// Parse KILL statement (MySQL/MariaDB)
21885    /// KILL [CONNECTION | QUERY] <id>
21886    fn parse_kill(&mut self) -> Result<Expression> {
21887        self.expect(TokenType::Kill)?;
21888
21889        // Check for optional kind: CONNECTION or QUERY
21890        let kind = if self.match_identifier("CONNECTION") {
21891            Some("CONNECTION".to_string())
21892        } else if self.match_identifier("QUERY") {
21893            Some("QUERY".to_string())
21894        } else {
21895            None
21896        };
21897
21898        // Parse the target (process ID - usually a number or string)
21899        let this = self.parse_primary()?;
21900
21901        Ok(Expression::Kill(Box::new(Kill { this, kind })))
21902    }
21903
21904    /// Parse EXEC/EXECUTE statement (TSQL stored procedure call)
21905    /// EXEC [schema.]procedure_name [@param=value, ...]
21906    fn parse_execute(&mut self) -> Result<Expression> {
21907        self.expect(TokenType::Execute)?;
21908
21909        // Dynamic SQL: EXEC(@sql) or EXEC (@sql)
21910        let this = if self.check(TokenType::LParen) {
21911            self.skip(); // consume (
21912            let expr = self
21913                .parse_disjunction()?
21914                .unwrap_or(Expression::Null(crate::expressions::Null));
21915            self.expect(TokenType::RParen)?;
21916            Expression::Paren(Box::new(crate::expressions::Paren {
21917                this: expr,
21918                trailing_comments: Vec::new(),
21919            }))
21920        } else {
21921            // Parse procedure name (can be qualified: schema.proc_name)
21922            let proc_name = self.parse_table_ref()?;
21923            Expression::Table(Box::new(proc_name))
21924        };
21925
21926        // Parse optional parameters: @param=value [OUTPUT], ...
21927        let mut parameters = Vec::new();
21928
21929        // Check if there are parameters (starts with @ or identifier)
21930        while self.check(TokenType::Var) || self.check(TokenType::Parameter) {
21931            // Get the parameter name (starts with @)
21932            let token = self.advance();
21933            let param_name = if token.text.starts_with('@') {
21934                token.text.clone()
21935            } else {
21936                format!("@{}", token.text)
21937            };
21938
21939            // Check for = (named parameter) or positional parameter
21940            if self.match_token(TokenType::Eq) {
21941                // Named parameter: @param = value
21942                let value = self.parse_primary()?;
21943                let output = self.match_token(TokenType::Output);
21944                parameters.push(ExecuteParameter {
21945                    name: param_name,
21946                    value,
21947                    positional: false,
21948                    output,
21949                });
21950            } else {
21951                // Positional parameter: @var (no = sign)
21952                let output = self.match_token(TokenType::Output);
21953                parameters.push(ExecuteParameter {
21954                    name: param_name.clone(),
21955                    value: Expression::boxed_column(Column {
21956                        name: Identifier::new(&param_name),
21957                        table: None,
21958                        join_mark: false,
21959                        trailing_comments: Vec::new(),
21960                        span: None,
21961                        inferred_type: None,
21962                    }),
21963                    positional: true,
21964                    output,
21965                });
21966            }
21967
21968            // Check for comma to continue
21969            if !self.match_token(TokenType::Comma) {
21970                break;
21971            }
21972        }
21973
21974        // TSQL: WITH RESULT SETS ((...), ...) or WITH RECOMPILE etc.
21975        let suffix = if self.check(TokenType::With) {
21976            let start = self.current;
21977            // Collect remaining tokens until semicolon or end
21978            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21979                self.skip();
21980            }
21981            Some(self.tokens_to_sql(start, self.current))
21982        } else {
21983            None
21984        };
21985
21986        Ok(Expression::Execute(Box::new(ExecuteStatement {
21987            this,
21988            parameters,
21989            suffix,
21990        })))
21991    }
21992
21993    /// Parse GRANT statement
21994    /// GRANT <privileges> ON [<kind>] <object> TO <principals> [WITH GRANT OPTION]
21995    fn parse_grant(&mut self) -> Result<Expression> {
21996        self.expect(TokenType::Grant)?;
21997
21998        // ClickHouse: GRANT can grant roles (no ON clause), grant privileges (has ON clause),
21999        // or use complex syntax. If we see TO before ON, treat as command.
22000        // Also: multi-privilege grants (multiple ON), wildcard grants (test*.*),
22001        // WITH REPLACE OPTION all parse as commands.
22002        if matches!(
22003            self.config.dialect,
22004            Some(crate::dialects::DialectType::ClickHouse)
22005        ) {
22006            // Save position after GRANT keyword
22007            let saved_pos = self.current;
22008            // Scan ahead to check grant structure
22009            let mut depth = 0i32;
22010            let mut on_count = 0;
22011            let mut found_to = false;
22012            let mut has_star_in_name = false;
22013            let mut has_replace_option = false;
22014            let mut i = self.current;
22015            while i < self.tokens.len() && self.tokens[i].token_type != TokenType::Semicolon {
22016                match self.tokens[i].token_type {
22017                    TokenType::LParen => depth += 1,
22018                    TokenType::RParen => depth -= 1,
22019                    TokenType::On if depth == 0 => on_count += 1,
22020                    TokenType::To if depth == 0 => {
22021                        found_to = true;
22022                    }
22023                    TokenType::Star if depth == 0 && on_count > 0 && !found_to => {
22024                        // Check if star is part of a wildcard name (e.g., test*.*)
22025                        if i > 0
22026                            && self.tokens[i - 1].token_type != TokenType::Dot
22027                            && self.tokens[i - 1].token_type != TokenType::On
22028                        {
22029                            has_star_in_name = true;
22030                        }
22031                    }
22032                    TokenType::Replace if depth == 0 && found_to => {
22033                        has_replace_option = true;
22034                    }
22035                    _ => {}
22036                }
22037                i += 1;
22038            }
22039            if (found_to && on_count == 0) || on_count > 1 || has_star_in_name || has_replace_option
22040            {
22041                // Role grant, multi-privilege grant, wildcard grant, or REPLACE OPTION — parse as command
22042                self.current = saved_pos;
22043                return self
22044                    .parse_command()?
22045                    .ok_or_else(|| self.parse_error("Failed to parse GRANT statement"));
22046            }
22047            self.current = saved_pos;
22048        }
22049
22050        // Parse privileges (e.g., SELECT, INSERT, UPDATE)
22051        let privileges = self.parse_privileges()?;
22052
22053        // Expect ON
22054        self.expect(TokenType::On)?;
22055
22056        // Parse optional kind (TABLE, SCHEMA, FUNCTION, etc.)
22057        let kind = self.parse_object_kind()?;
22058
22059        // Parse securable (the object) - may be dot-separated qualified name
22060        let securable = self.parse_securable_name()?;
22061
22062        // Parse optional function parameter types: func(type1, type2, ...)
22063        let function_params = if self.check(TokenType::LParen) {
22064            self.parse_function_param_types()?
22065        } else {
22066            Vec::new()
22067        };
22068
22069        // Expect TO
22070        self.expect(TokenType::To)?;
22071
22072        // Parse principals
22073        let principals = self.parse_principals()?;
22074
22075        // Check for WITH GRANT OPTION
22076        let grant_option = self.match_token(TokenType::With)
22077            && self.check(TokenType::Grant)
22078            && {
22079                self.skip();
22080                self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTION")
22081            }
22082            && {
22083                self.skip();
22084                true
22085            };
22086
22087        // Check for TSQL AS principal clause
22088        let as_principal = if self.match_token(TokenType::As) {
22089            let name = self.expect_identifier_or_keyword()?;
22090            Some(Identifier::new(name))
22091        } else {
22092            None
22093        };
22094
22095        Ok(Expression::Grant(Box::new(Grant {
22096            privileges,
22097            kind,
22098            securable,
22099            function_params,
22100            principals,
22101            grant_option,
22102            as_principal,
22103        })))
22104    }
22105
22106    /// Parse REVOKE statement
22107    /// REVOKE [GRANT OPTION FOR] <privileges> ON [<kind>] <object> FROM <principals> [CASCADE]
22108    fn parse_revoke(&mut self) -> Result<Expression> {
22109        self.expect(TokenType::Revoke)?;
22110
22111        // ClickHouse: REVOKE role FROM user (no ON clause), multi-privilege, or wildcard — parse as command
22112        if matches!(
22113            self.config.dialect,
22114            Some(crate::dialects::DialectType::ClickHouse)
22115        ) {
22116            let saved_pos = self.current;
22117            let mut depth = 0i32;
22118            let mut on_count = 0;
22119            let mut found_from = false;
22120            let mut has_star_in_name = false;
22121            let mut i = self.current;
22122            while i < self.tokens.len() && self.tokens[i].token_type != TokenType::Semicolon {
22123                match self.tokens[i].token_type {
22124                    TokenType::LParen => depth += 1,
22125                    TokenType::RParen => depth -= 1,
22126                    TokenType::On if depth == 0 => on_count += 1,
22127                    TokenType::From if depth == 0 => {
22128                        found_from = true;
22129                    }
22130                    TokenType::Star if depth == 0 && on_count > 0 && !found_from => {
22131                        if i > 0
22132                            && self.tokens[i - 1].token_type != TokenType::Dot
22133                            && self.tokens[i - 1].token_type != TokenType::On
22134                        {
22135                            has_star_in_name = true;
22136                        }
22137                    }
22138                    _ => {}
22139                }
22140                i += 1;
22141            }
22142            if (found_from && on_count == 0) || on_count > 1 || has_star_in_name {
22143                self.current = saved_pos;
22144                return self
22145                    .parse_command()?
22146                    .ok_or_else(|| self.parse_error("Failed to parse REVOKE statement"));
22147            }
22148            self.current = saved_pos;
22149        }
22150
22151        // Check for GRANT OPTION FOR
22152        let grant_option = if self.check(TokenType::Grant) {
22153            self.skip();
22154            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTION") {
22155                self.skip();
22156                self.expect(TokenType::For)?;
22157                true
22158            } else {
22159                return Err(self.parse_error("Expected OPTION after GRANT in REVOKE"));
22160            }
22161        } else {
22162            false
22163        };
22164
22165        // Parse privileges
22166        let privileges = self.parse_privileges()?;
22167
22168        // Expect ON
22169        self.expect(TokenType::On)?;
22170
22171        // Parse optional kind
22172        let kind = self.parse_object_kind()?;
22173
22174        // Parse securable - may be dot-separated qualified name
22175        let securable = self.parse_securable_name()?;
22176
22177        // Parse optional function parameter types: func(type1, type2, ...)
22178        let function_params = if self.check(TokenType::LParen) {
22179            self.parse_function_param_types()?
22180        } else {
22181            Vec::new()
22182        };
22183
22184        // Expect FROM
22185        self.expect(TokenType::From)?;
22186
22187        // Parse principals
22188        let principals = self.parse_principals()?;
22189
22190        // Check for CASCADE or RESTRICT
22191        let cascade = self.match_token(TokenType::Cascade);
22192        let restrict = if !cascade {
22193            self.match_token(TokenType::Restrict)
22194        } else {
22195            false
22196        };
22197
22198        Ok(Expression::Revoke(Box::new(Revoke {
22199            privileges,
22200            kind,
22201            securable,
22202            function_params,
22203            principals,
22204            grant_option,
22205            cascade,
22206            restrict,
22207        })))
22208    }
22209
22210    /// Parse privilege list for GRANT/REVOKE
22211    /// Handles multi-word privileges like "ALL PRIVILEGES" and column-level privileges like "SELECT(col1, col2)"
22212    fn parse_privileges(&mut self) -> Result<Vec<Privilege>> {
22213        let mut privileges = Vec::new();
22214        loop {
22215            let mut priv_parts = Vec::new();
22216            // Collect privilege words until we hit ON, comma, LParen, or similar terminator
22217            while !self.is_at_end() {
22218                if self.check(TokenType::On)
22219                    || self.check(TokenType::Comma)
22220                    || self.check(TokenType::LParen)
22221                {
22222                    break;
22223                }
22224                if self.is_identifier_or_keyword_token() {
22225                    priv_parts.push(self.advance().text.to_ascii_uppercase());
22226                } else {
22227                    break;
22228                }
22229            }
22230            if priv_parts.is_empty() {
22231                break;
22232            }
22233            let priv_name = priv_parts.join(" ");
22234
22235            // Check for column list in parentheses: SELECT(col1, col2)
22236            let columns = if self.match_token(TokenType::LParen) {
22237                let mut cols = Vec::new();
22238                loop {
22239                    // Parse column name (identifier)
22240                    if self.is_identifier_or_keyword_token() {
22241                        cols.push(self.advance().text.to_string());
22242                    } else if self.check(TokenType::RParen) {
22243                        break;
22244                    } else {
22245                        break;
22246                    }
22247                    if !self.match_token(TokenType::Comma) {
22248                        break;
22249                    }
22250                }
22251                self.expect(TokenType::RParen)?;
22252                cols
22253            } else {
22254                Vec::new()
22255            };
22256
22257            privileges.push(Privilege {
22258                name: priv_name,
22259                columns,
22260            });
22261            if !self.match_token(TokenType::Comma) {
22262                break;
22263            }
22264        }
22265        Ok(privileges)
22266    }
22267
22268    /// Parse object kind (TABLE, SCHEMA, FUNCTION, PROCEDURE, SEQUENCE, etc.)
22269    fn parse_object_kind(&mut self) -> Result<Option<String>> {
22270        if self.check(TokenType::Table) {
22271            self.skip();
22272            Ok(Some("TABLE".to_string()))
22273        } else if self.check(TokenType::Schema) {
22274            self.skip();
22275            Ok(Some("SCHEMA".to_string()))
22276        } else if self.check(TokenType::Database) {
22277            self.skip();
22278            Ok(Some("DATABASE".to_string()))
22279        } else if self.check(TokenType::Function) {
22280            self.skip();
22281            Ok(Some("FUNCTION".to_string()))
22282        } else if self.check(TokenType::View) {
22283            self.skip();
22284            Ok(Some("VIEW".to_string()))
22285        } else if self.check(TokenType::Procedure) {
22286            self.skip();
22287            Ok(Some("PROCEDURE".to_string()))
22288        } else if self.check(TokenType::Sequence) {
22289            self.skip();
22290            Ok(Some("SEQUENCE".to_string()))
22291        } else if self.check(TokenType::Warehouse) {
22292            self.skip();
22293            Ok(Some("WAREHOUSE".to_string()))
22294        } else if self.check_identifier("STAGE")
22295            || self.check_identifier("INTEGRATION")
22296            || self.check_identifier("TASK")
22297            || self.check_identifier("STREAM")
22298            || self.check_identifier("PIPE")
22299            || self.check_identifier("TAG")
22300            || self.check_identifier("SHARE")
22301        {
22302            let kind = self.advance().text.to_ascii_uppercase();
22303            Ok(Some(kind))
22304        } else if self.check_identifier("FILE")
22305            && self.current + 1 < self.tokens.len()
22306            && self.tokens[self.current + 1]
22307                .text
22308                .eq_ignore_ascii_case("FORMAT")
22309        {
22310            self.skip(); // consume FILE
22311            self.skip(); // consume FORMAT
22312            Ok(Some("FILE FORMAT".to_string()))
22313        } else if self.check_identifier("NETWORK")
22314            && self.current + 1 < self.tokens.len()
22315            && self.tokens[self.current + 1]
22316                .text
22317                .eq_ignore_ascii_case("POLICY")
22318        {
22319            self.skip(); // consume NETWORK
22320            self.skip(); // consume POLICY
22321            Ok(Some("NETWORK POLICY".to_string()))
22322        } else {
22323            Ok(None)
22324        }
22325    }
22326
22327    /// Parse principal list for GRANT/REVOKE
22328    fn parse_principals(&mut self) -> Result<Vec<GrantPrincipal>> {
22329        let mut principals = Vec::new();
22330        loop {
22331            // Check for ROLE keyword (TokenType::Var with text "ROLE")
22332            let is_role =
22333                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLE") {
22334                    self.skip();
22335                    true
22336                } else {
22337                    false
22338                };
22339            // Check for GROUP keyword (Redshift) - TokenType::Group
22340            let is_group = if !is_role && self.check(TokenType::Group) {
22341                self.skip();
22342                true
22343            } else {
22344                false
22345            };
22346            // Check for SHARE keyword (Snowflake)
22347            let is_share = if !is_role && !is_group && self.check_identifier("SHARE") {
22348                self.skip();
22349                true
22350            } else {
22351                false
22352            };
22353            // Parse principal name (with quoted flag preserved for backtick-quoted identifiers)
22354            let name = self.expect_identifier_or_keyword_with_quoted()?;
22355            principals.push(GrantPrincipal {
22356                name,
22357                is_role,
22358                is_group,
22359                is_share,
22360            });
22361            if !self.match_token(TokenType::Comma) {
22362                break;
22363            }
22364        }
22365        Ok(principals)
22366    }
22367
22368    /// Parse a securable name (potentially dot-separated qualified name)
22369    /// e.g., "mydb.myschema.ADD5" -> Identifier("mydb.myschema.ADD5")
22370    fn parse_securable_name(&mut self) -> Result<Identifier> {
22371        // Accept * as a name part (e.g., GRANT ON *.* or GRANT ON db.*)
22372        let first = if self.match_token(TokenType::Star) {
22373            "*".to_string()
22374        } else {
22375            self.expect_identifier_or_keyword()?
22376        };
22377        let mut parts = vec![first];
22378
22379        while self.match_token(TokenType::Dot) {
22380            let next = if self.match_token(TokenType::Star) {
22381                "*".to_string()
22382            } else {
22383                self.expect_identifier_or_keyword()?
22384            };
22385            parts.push(next);
22386        }
22387
22388        Ok(Identifier::new(parts.join(".")))
22389    }
22390
22391    /// Parse function parameter types for GRANT/REVOKE ON FUNCTION
22392    /// e.g., "(number, varchar)" -> vec!["number", "varchar"]
22393    fn parse_function_param_types(&mut self) -> Result<Vec<String>> {
22394        self.expect(TokenType::LParen)?;
22395
22396        let mut params = Vec::new();
22397        if !self.check(TokenType::RParen) {
22398            loop {
22399                // Parse parameter type - can be a keyword (INT, VARCHAR) or identifier
22400                let param_type = self.expect_identifier_or_keyword()?;
22401                params.push(param_type);
22402                if !self.match_token(TokenType::Comma) {
22403                    break;
22404                }
22405            }
22406        }
22407
22408        self.expect(TokenType::RParen)?;
22409        Ok(params)
22410    }
22411
22412    /// Parse COMMENT ON statement
22413    fn parse_comment(&mut self) -> Result<Expression> {
22414        self.expect(TokenType::Comment)?;
22415
22416        // Check for IF EXISTS
22417        let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
22418
22419        // Expect ON
22420        self.expect(TokenType::On)?;
22421
22422        // Check for MATERIALIZED (can be TokenType::Materialized or TokenType::Var)
22423        let materialized = if self.match_token(TokenType::Materialized) {
22424            true
22425        } else if self.check(TokenType::Var)
22426            && self.peek().text.eq_ignore_ascii_case("MATERIALIZED")
22427        {
22428            self.skip();
22429            true
22430        } else {
22431            false
22432        };
22433
22434        // Parse the object kind (COLUMN, TABLE, DATABASE, PROCEDURE, etc.)
22435        let kind = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
22436
22437        // Parse the object name (can be qualified like schema.table.column)
22438        // For PROCEDURE/FUNCTION, we need to handle the parameter list like my_proc(integer, integer)
22439        let this = if kind == "PROCEDURE" || kind == "FUNCTION" {
22440            // Parse name possibly with parameter types, preserving original case
22441            let name_token = self.advance();
22442            let mut name_str = name_token.text.clone();
22443
22444            // Parse additional qualified parts
22445            while self.match_token(TokenType::Dot) {
22446                let next = self.advance();
22447                name_str.push('.');
22448                name_str.push_str(&next.text);
22449            }
22450
22451            // Check for parameter types in parentheses
22452            if self.match_token(TokenType::LParen) {
22453                name_str.push('(');
22454                let mut first = true;
22455                while !self.check(TokenType::RParen) && !self.is_at_end() {
22456                    if !first {
22457                        name_str.push_str(", ");
22458                    }
22459                    first = false;
22460                    let param_token = self.advance();
22461                    name_str.push_str(&param_token.text);
22462                    self.match_token(TokenType::Comma);
22463                }
22464                self.expect(TokenType::RParen)?;
22465                name_str.push(')');
22466            }
22467
22468            Expression::Identifier(Identifier::new(name_str))
22469        } else {
22470            self.parse_qualified_name()?
22471        };
22472
22473        // Expect IS
22474        if self.check(TokenType::Is) {
22475            self.skip();
22476        } else {
22477            return Err(self.parse_error("Expected IS in COMMENT ON statement"));
22478        }
22479
22480        // Parse the comment expression (usually a string literal)
22481        let expression = self.parse_primary()?;
22482
22483        Ok(Expression::Comment(Box::new(Comment {
22484            this,
22485            kind,
22486            expression,
22487            exists,
22488            materialized,
22489        })))
22490    }
22491
22492    /// Parse SET statement
22493    fn parse_set(&mut self) -> Result<Expression> {
22494        self.expect(TokenType::Set)?;
22495
22496        let mut items = Vec::new();
22497
22498        // ClickHouse: SET DEFAULT ROLE ... TO user - parse as command
22499        if matches!(
22500            self.config.dialect,
22501            Some(crate::dialects::DialectType::ClickHouse)
22502        ) && self.check(TokenType::Default)
22503        {
22504            let mut parts = vec!["SET".to_string()];
22505            while !self.is_at_end() && self.peek().token_type != TokenType::Semicolon {
22506                parts.push(self.advance().text.clone());
22507            }
22508            return Ok(Expression::Command(Box::new(crate::expressions::Command {
22509                this: parts.join(" "),
22510            })));
22511        }
22512
22513        // Teradata: SET QUERY_BAND = ... [UPDATE] [FOR scope]
22514        if matches!(
22515            self.config.dialect,
22516            Some(crate::dialects::DialectType::Teradata)
22517        ) && self.match_identifier("QUERY_BAND")
22518        {
22519            return self.parse_query_band();
22520        }
22521
22522        // Handle MySQL SET CHARACTER SET / SET NAMES
22523        if self.match_identifier("CHARACTER") {
22524            // SET CHARACTER SET <charset> | SET CHARACTER SET DEFAULT
22525            self.expect(TokenType::Set)?;
22526            let value = if self.match_token(TokenType::Default) {
22527                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22528            } else {
22529                self.parse_primary()?
22530            };
22531            items.push(SetItem {
22532                name: Expression::Identifier(Identifier::new("CHARACTER SET".to_string())),
22533                value,
22534                kind: None,
22535                no_equals: false,
22536            });
22537            return Ok(Expression::SetStatement(Box::new(SetStatement { items })));
22538        }
22539
22540        if self.match_identifier("NAMES") {
22541            // SET NAMES <charset> [COLLATE <collation>] | SET NAMES DEFAULT
22542            let value = if self.match_token(TokenType::Default) {
22543                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22544            } else {
22545                self.parse_primary()?
22546            };
22547            // Check for optional COLLATE clause
22548            let collation = if self.match_identifier("COLLATE") {
22549                Some(self.parse_primary()?)
22550            } else {
22551                None
22552            };
22553            items.push(SetItem {
22554                name: Expression::Identifier(Identifier::new("NAMES".to_string())),
22555                value,
22556                kind: None,
22557                no_equals: false,
22558            });
22559            if let Some(coll) = collation {
22560                items.push(SetItem {
22561                    name: Expression::Identifier(Identifier::new("COLLATE".to_string())),
22562                    value: coll,
22563                    kind: None,
22564                    no_equals: false,
22565                });
22566            }
22567            return Ok(Expression::SetStatement(Box::new(SetStatement { items })));
22568        }
22569
22570        // Track whether SET VAR/VARIABLE was used (only first item gets the VARIABLE kind)
22571        let mut set_is_variable = if self.check(TokenType::Var) {
22572            let text = self.peek().text.to_uppercase();
22573            if text == "VARIABLE" || text == "VAR" {
22574                // Look ahead: VAR/VARIABLE should be followed by another name, not by = or TO
22575                if let Some(next) = self.tokens.get(self.current + 1) {
22576                    if next.token_type != TokenType::Eq
22577                        && next.token_type != TokenType::To
22578                        && next.token_type != TokenType::ColonEq
22579                    {
22580                        self.skip(); // consume VAR/VARIABLE
22581                        true
22582                    } else {
22583                        false
22584                    }
22585                } else {
22586                    false
22587                }
22588            } else {
22589                false
22590            }
22591        } else {
22592            false
22593        };
22594
22595        loop {
22596            // Check for GLOBAL, LOCAL, SESSION, PERSIST, PERSIST_ONLY modifiers
22597            // LOCAL is a token type, others are identifiers
22598            let kind = if self.match_identifier("GLOBAL") {
22599                Some("GLOBAL".to_string())
22600            } else if self.match_token(TokenType::Local) {
22601                Some("LOCAL".to_string())
22602            } else if self.match_identifier("SESSION") {
22603                Some("SESSION".to_string())
22604            } else if self.match_identifier("PERSIST") {
22605                Some("PERSIST".to_string())
22606            } else if self.match_identifier("PERSIST_ONLY") {
22607                Some("PERSIST_ONLY".to_string())
22608            } else if set_is_variable {
22609                set_is_variable = false; // Only first item gets VARIABLE kind
22610                Some("VARIABLE".to_string())
22611            } else {
22612                None
22613            };
22614
22615            // Check for SET [GLOBAL|SESSION] TRANSACTION (MySQL)
22616            if self.match_token(TokenType::Transaction) {
22617                // Parse transaction characteristics (ISOLATION LEVEL, READ ONLY, READ WRITE)
22618                let mut characteristics = Vec::new();
22619                loop {
22620                    let mut char_tokens = Vec::new();
22621                    // Parse ISOLATION LEVEL ... or READ ONLY/WRITE
22622                    // Must handle keywords like ONLY, REPEATABLE, SERIALIZABLE, etc.
22623                    while !self.is_at_end()
22624                        && !self.check(TokenType::Comma)
22625                        && !self.check(TokenType::Semicolon)
22626                    {
22627                        // Allow identifiers and common transaction-related keywords
22628                        if self.is_identifier_token()
22629                            || self.is_safe_keyword_as_identifier()
22630                            || self.check(TokenType::Only)
22631                            || self.check(TokenType::Repeatable)
22632                        {
22633                            char_tokens.push(self.advance().text);
22634                        } else {
22635                            break;
22636                        }
22637                    }
22638                    if !char_tokens.is_empty() {
22639                        characteristics.push(char_tokens.join(" "));
22640                    }
22641                    if !self.match_token(TokenType::Comma) {
22642                        break;
22643                    }
22644                }
22645
22646                let name = Expression::Identifier(Identifier::new("TRANSACTION".to_string()));
22647                let value = if characteristics.is_empty() {
22648                    Expression::Identifier(Identifier::new("".to_string()))
22649                } else {
22650                    Expression::Identifier(Identifier::new(characteristics.join(", ")))
22651                };
22652
22653                items.push(SetItem {
22654                    name,
22655                    value,
22656                    kind,
22657                    no_equals: false,
22658                });
22659                break;
22660            }
22661
22662            // Parse variable name - use a simple approach to avoid expression parsing issues
22663            // Variable names can be dotted identifiers or keywords used as names
22664            let name = {
22665                if self.check(TokenType::AtAt) {
22666                    // @@SCOPE.variable or @@variable syntax (MySQL system variables)
22667                    self.skip(); // consume @@
22668                    let mut name_str = "@@".to_string();
22669                    let first = self.advance().text.clone();
22670                    name_str.push_str(&first);
22671                    // Handle @@scope.variable (e.g., @@GLOBAL.max_connections)
22672                    while self.match_token(TokenType::Dot) {
22673                        let next = self.advance().text.clone();
22674                        name_str.push('.');
22675                        name_str.push_str(&next);
22676                    }
22677                    Expression::Identifier(Identifier::new(name_str))
22678                } else if self.check(TokenType::DAt) {
22679                    // @variable syntax (MySQL user variables)
22680                    self.skip(); // consume @
22681                    let mut name_str = "@".to_string();
22682                    let first = self.advance().text.clone();
22683                    name_str.push_str(&first);
22684                    Expression::Identifier(Identifier::new(name_str))
22685                } else if self.check(TokenType::LParen) {
22686                    // Tuple of variable names: SET VARIABLE (v1, v2) = (SELECT ...)
22687                    self.skip(); // consume (
22688                    let mut vars = Vec::new();
22689                    loop {
22690                        let var_name = self.advance().text.clone();
22691                        vars.push(Expression::Column(Box::new(Column {
22692                            name: Identifier::new(var_name),
22693                            table: None,
22694                            join_mark: false,
22695                            trailing_comments: Vec::new(),
22696                            span: None,
22697                            inferred_type: None,
22698                        })));
22699                        if !self.match_token(TokenType::Comma) {
22700                            break;
22701                        }
22702                    }
22703                    self.expect(TokenType::RParen)?;
22704                    Expression::Tuple(Box::new(crate::expressions::Tuple { expressions: vars }))
22705                } else {
22706                    let first = self.advance().text.clone();
22707                    let mut name_str = first;
22708                    // Handle dotted identifiers (e.g., schema.variable)
22709                    while self.match_token(TokenType::Dot) {
22710                        let next = self.advance().text.clone();
22711                        name_str.push('.');
22712                        name_str.push_str(&next);
22713                    }
22714                    // Handle Hive-style colon-separated names (e.g., hiveconf:some_var)
22715                    // But not := which is assignment
22716                    while self.check(TokenType::Colon) && !self.check_next(TokenType::Eq) {
22717                        self.skip(); // consume :
22718                        let next = self.advance().text.clone();
22719                        name_str.push(':');
22720                        name_str.push_str(&next);
22721                    }
22722                    Expression::Identifier(Identifier::new(name_str))
22723                }
22724            };
22725
22726            // Expect = or := or TO
22727            if self.match_token(TokenType::Eq) || self.match_token(TokenType::ColonEq) {
22728                // ok - standard assignment
22729            } else if self.match_token(TokenType::To) {
22730                // PostgreSQL uses SET var TO value
22731            } else if self.is_at_end()
22732                || self.check(TokenType::Semicolon)
22733                || self.check(TokenType::Comma)
22734            {
22735                // SET x ON/OFF without = (TSQL: SET XACT_ABORT ON)
22736                // The ON/OFF was already parsed as part of the name expression
22737                // Handle as a name-only set (value is empty)
22738                items.push(SetItem {
22739                    name,
22740                    value: Expression::Identifier(Identifier::new("".to_string())),
22741                    kind,
22742                    no_equals: false,
22743                });
22744                if !self.match_token(TokenType::Comma) {
22745                    break;
22746                }
22747                continue;
22748            } else {
22749                // Check if the next token looks like a value (ON/OFF without =)
22750                // TSQL: SET XACT_ABORT ON, SET NOCOUNT ON
22751                if self.check(TokenType::On) || self.check_keyword_text("OFF") {
22752                    let val = self.advance().text;
22753                    // Include ON/OFF in the name so generator doesn't add "="
22754                    let name_with_val = match &name {
22755                        Expression::Column(col) => format!("{} {}", col.name.name, val),
22756                        Expression::Identifier(id) => format!("{} {}", id.name, val),
22757                        _ => val.clone(),
22758                    };
22759                    items.push(SetItem {
22760                        name: Expression::Identifier(Identifier::new(name_with_val)),
22761                        value: Expression::Identifier(Identifier::new("".to_string())),
22762                        kind,
22763                        no_equals: false,
22764                    });
22765                    if !self.match_token(TokenType::Comma) {
22766                        break;
22767                    }
22768                    continue;
22769                }
22770                // TSQL/Generic: SET key value (without = or TO)
22771                // Parse the next token as the value
22772                if !self.is_at_end() && !self.check(TokenType::Semicolon) {
22773                    let value = self.parse_expression()?;
22774                    items.push(SetItem {
22775                        name,
22776                        value,
22777                        kind,
22778                        no_equals: true,
22779                    });
22780                    if !self.match_token(TokenType::Comma) {
22781                        break;
22782                    }
22783                    continue;
22784                }
22785                return Err(self.parse_error("Expected '=' or 'TO' in SET statement"));
22786            }
22787
22788            // Parse value - handle ON/OFF keywords as identifiers (MySQL: SET autocommit = ON)
22789            let value = if self.check(TokenType::On) || self.check_keyword_text("OFF") {
22790                Expression::Identifier(Identifier::new(self.advance().text.clone()))
22791            } else if self.match_token(TokenType::Default) {
22792                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22793            } else {
22794                self.parse_expression()?
22795            };
22796
22797            items.push(SetItem {
22798                name,
22799                value,
22800                kind,
22801                no_equals: false,
22802            });
22803
22804            if !self.match_token(TokenType::Comma) {
22805                break;
22806            }
22807        }
22808
22809        Ok(Expression::SetStatement(Box::new(SetStatement { items })))
22810    }
22811
22812    /// Parse Teradata SET QUERY_BAND statement
22813    fn parse_query_band(&mut self) -> Result<Expression> {
22814        self.expect(TokenType::Eq)?;
22815
22816        let value = if self.match_identifier("NONE") {
22817            Expression::Var(Box::new(Var {
22818                this: "NONE".to_string(),
22819            }))
22820        } else if self.check(TokenType::String) {
22821            Expression::Literal(Box::new(Literal::String(self.expect_string()?)))
22822        } else {
22823            self.parse_primary()?
22824        };
22825
22826        let update = if self.match_token(TokenType::Update) || self.match_identifier("UPDATE") {
22827            Some(Box::new(Expression::Boolean(BooleanLiteral {
22828                value: true,
22829            })))
22830        } else {
22831            None
22832        };
22833
22834        let _ = self.match_token(TokenType::For);
22835
22836        let scope = if self.match_token(TokenType::Session) || self.match_identifier("SESSION") {
22837            if self.match_identifier("VOLATILE") {
22838                Some("SESSION VOLATILE".to_string())
22839            } else {
22840                Some("SESSION".to_string())
22841            }
22842        } else if self.match_token(TokenType::Transaction) || self.match_identifier("TRANSACTION") {
22843            Some("TRANSACTION".to_string())
22844        } else if self.match_identifier("VOLATILE") {
22845            Some("VOLATILE".to_string())
22846        } else {
22847            None
22848        };
22849
22850        Ok(Expression::QueryBand(Box::new(QueryBand {
22851            this: Box::new(value),
22852            scope: scope.map(|s| Box::new(Expression::Var(Box::new(Var { this: s })))),
22853            update,
22854        })))
22855    }
22856
22857    /// Parse FETCH FIRST/NEXT clause
22858    fn parse_fetch(&mut self) -> Result<Fetch> {
22859        // FETCH [FIRST|NEXT] [count] [PERCENT] [ROW|ROWS] [ONLY|WITH TIES]
22860
22861        // FIRST or NEXT
22862        let direction = if self.match_token(TokenType::First) {
22863            "FIRST".to_string()
22864        } else if self.match_token(TokenType::Next) {
22865            "NEXT".to_string()
22866        } else {
22867            "FIRST".to_string() // Default
22868        };
22869
22870        // Optional count - but check if next token is ROW/ROWS/PERCENT/ONLY (no count)
22871        let count = if !self.check(TokenType::Row)
22872            && !self.check(TokenType::Rows)
22873            && !self.check(TokenType::Percent)
22874            && !self.check(TokenType::Only)
22875        {
22876            // Accept number, parenthesized expression, or TSQL @variable (Var token)
22877            if self.check(TokenType::Number)
22878                || self.check(TokenType::LParen)
22879                || self.check(TokenType::DAt)
22880                || self.check(TokenType::Var)
22881            {
22882                Some(self.parse_primary()?)
22883            } else {
22884                None
22885            }
22886        } else {
22887            None
22888        };
22889
22890        // PERCENT modifier
22891        let percent = self.match_token(TokenType::Percent);
22892
22893        // ROW or ROWS
22894        let rows = self.match_token(TokenType::Row) || self.match_token(TokenType::Rows);
22895
22896        // ONLY or WITH TIES
22897        self.match_token(TokenType::Only);
22898        let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
22899
22900        Ok(Fetch {
22901            direction,
22902            count,
22903            percent,
22904            rows,
22905            with_ties,
22906        })
22907    }
22908
22909    /// Parse a qualified name (schema.table.column or just table)
22910    fn parse_qualified_name(&mut self) -> Result<Expression> {
22911        let first = self.expect_identifier_or_keyword()?;
22912        let mut parts = vec![first];
22913
22914        while self.match_token(TokenType::Dot) {
22915            let next = self.expect_identifier_or_keyword()?;
22916            parts.push(next);
22917        }
22918
22919        if parts.len() == 1 {
22920            Ok(Expression::Identifier(Identifier::new(parts.remove(0))))
22921        } else if parts.len() == 2 {
22922            Ok(Expression::boxed_column(Column {
22923                table: Some(Identifier::new(parts[0].clone())),
22924                name: Identifier::new(parts[1].clone()),
22925                join_mark: false,
22926                trailing_comments: Vec::new(),
22927                span: None,
22928                inferred_type: None,
22929            }))
22930        } else {
22931            // For 3+ parts, create a Column with concatenated table parts
22932            let column_name = parts.pop().unwrap();
22933            let table_name = parts.join(".");
22934            Ok(Expression::boxed_column(Column {
22935                table: Some(Identifier::new(table_name)),
22936                name: Identifier::new(column_name),
22937                join_mark: false,
22938                trailing_comments: Vec::new(),
22939                span: None,
22940                inferred_type: None,
22941            }))
22942        }
22943    }
22944
22945    // ==================== Phase 4: Additional DDL Parsing ====================
22946
22947    /// Parse CREATE SCHEMA statement
22948    fn parse_create_schema(&mut self, leading_comments: Vec<String>) -> Result<Expression> {
22949        self.expect(TokenType::Schema)?;
22950
22951        let if_not_exists =
22952            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
22953        let name = self.parse_identifier_parts()?;
22954
22955        // Parse CLONE clause (Snowflake)
22956        let clone_from = if self.match_identifier("CLONE") {
22957            Some(self.parse_identifier_parts()?)
22958        } else {
22959            None
22960        };
22961
22962        // Parse AT/BEFORE clause for time travel (Snowflake)
22963        // Note: BEFORE is a keyword token, AT is an identifier
22964        let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
22965            let keyword = self.previous().text.to_ascii_uppercase();
22966            self.expect(TokenType::LParen)?;
22967            // Parse the content: OFFSET => value or TIMESTAMP => value
22968            let mut result = format!("{} (", keyword);
22969            let mut prev_token_type: Option<TokenType> = None;
22970            let mut paren_depth = 1; // Track nested parens
22971            while !self.is_at_end() && paren_depth > 0 {
22972                let token = self.advance();
22973                if token.token_type == TokenType::LParen {
22974                    paren_depth += 1;
22975                } else if token.token_type == TokenType::RParen {
22976                    paren_depth -= 1;
22977                    if paren_depth == 0 {
22978                        break; // Don't include the closing paren in result yet
22979                    }
22980                }
22981                // Smart spacing: no space after ( or => or - and no space before (
22982                let needs_space = !result.ends_with('(')
22983                    && prev_token_type != Some(TokenType::Arrow)
22984                    && prev_token_type != Some(TokenType::Dash)
22985                    && prev_token_type != Some(TokenType::LParen)
22986                    && token.token_type != TokenType::LParen; // no space before (
22987                if needs_space
22988                    && token.token_type != TokenType::RParen
22989                    && token.token_type != TokenType::Comma
22990                {
22991                    result.push(' ');
22992                }
22993                // Properly quote string literals
22994                if token.token_type == TokenType::String {
22995                    result.push('\'');
22996                    result.push_str(&token.text.replace('\'', "''"));
22997                    result.push('\'');
22998                } else {
22999                    result.push_str(&token.text);
23000                }
23001                if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma {
23002                    result.push(' ');
23003                }
23004                prev_token_type = Some(token.token_type);
23005            }
23006            result.push(')');
23007            Some(Expression::Raw(Raw { sql: result }))
23008        } else {
23009            None
23010        };
23011
23012        let authorization = if self.match_token(TokenType::Authorization) {
23013            Some(Identifier::new(self.expect_identifier()?))
23014        } else {
23015            None
23016        };
23017
23018        // Parse schema properties like DEFAULT COLLATE or WITH (properties)
23019        let mut properties = Vec::new();
23020
23021        // Parse WITH (prop1=val1, prop2=val2, ...) (Trino/Presto)
23022        if self.match_token(TokenType::With) {
23023            self.expect(TokenType::LParen)?;
23024            loop {
23025                // Parse property name (identifier or string)
23026                let prop_name = if self.check(TokenType::String) {
23027                    Expression::Literal(Box::new(Literal::String(self.expect_string()?)))
23028                } else {
23029                    Expression::Identifier(Identifier::new(self.expect_identifier_or_keyword()?))
23030                };
23031                self.expect(TokenType::Eq)?;
23032                // Parse property value
23033                let prop_value = self.parse_expression()?;
23034                // Create Property expression: key=value
23035                properties.push(Expression::Property(Box::new(Property {
23036                    this: Box::new(prop_name),
23037                    value: Some(Box::new(prop_value)),
23038                })));
23039                if !self.match_token(TokenType::Comma) {
23040                    break;
23041                }
23042            }
23043            self.expect(TokenType::RParen)?;
23044        }
23045
23046        // Parse DEFAULT COLLATE 'value' (BigQuery)
23047        if self.match_token(TokenType::Default) && self.match_token(TokenType::Collate) {
23048            // Parse the collation value (could be string literal or identifier)
23049            let collation = self.parse_primary()?;
23050            properties.push(Expression::CollateProperty(Box::new(CollateProperty {
23051                this: Box::new(collation),
23052                default: Some(Box::new(Expression::Boolean(BooleanLiteral {
23053                    value: true,
23054                }))),
23055            })));
23056        }
23057
23058        Ok(Expression::CreateSchema(Box::new(CreateSchema {
23059            name,
23060            if_not_exists,
23061            authorization,
23062            clone_from,
23063            at_clause,
23064            properties,
23065            leading_comments,
23066        })))
23067    }
23068
23069    /// Parse DROP SCHEMA statement
23070    fn parse_drop_schema(&mut self) -> Result<Expression> {
23071        self.expect(TokenType::Schema)?;
23072
23073        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23074        let name = Identifier::new(self.expect_identifier()?);
23075
23076        let cascade = self.match_token(TokenType::Cascade);
23077        if !cascade {
23078            self.match_token(TokenType::Restrict);
23079        }
23080
23081        Ok(Expression::DropSchema(Box::new(DropSchema {
23082            name,
23083            if_exists,
23084            cascade,
23085        })))
23086    }
23087
23088    /// Parse CREATE DATABASE statement
23089    fn parse_create_database(&mut self) -> Result<Expression> {
23090        self.expect(TokenType::Database)?;
23091
23092        let if_not_exists =
23093            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
23094        let name = Identifier::new(self.expect_identifier()?);
23095
23096        // Check for Snowflake CLONE clause
23097        let clone_from = if self.match_identifier("CLONE") {
23098            Some(Identifier::new(self.expect_identifier()?))
23099        } else {
23100            None
23101        };
23102
23103        // Parse AT/BEFORE clause for time travel (Snowflake)
23104        // Note: BEFORE is a keyword token, AT is an identifier
23105        let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
23106            let keyword = self.previous().text.to_ascii_uppercase();
23107            self.expect(TokenType::LParen)?;
23108            // Parse the content: OFFSET => value or TIMESTAMP => value
23109            let mut result = format!("{} (", keyword);
23110            let mut prev_token_type: Option<TokenType> = None;
23111            let mut paren_depth = 1; // Track nested parens
23112            while !self.is_at_end() && paren_depth > 0 {
23113                let token = self.advance();
23114                if token.token_type == TokenType::LParen {
23115                    paren_depth += 1;
23116                } else if token.token_type == TokenType::RParen {
23117                    paren_depth -= 1;
23118                    if paren_depth == 0 {
23119                        break; // Don't include the closing paren in result yet
23120                    }
23121                }
23122                // Smart spacing: no space after ( or => or - and no space before (
23123                let needs_space = !result.ends_with('(')
23124                    && prev_token_type != Some(TokenType::Arrow)
23125                    && prev_token_type != Some(TokenType::Dash)
23126                    && prev_token_type != Some(TokenType::LParen)
23127                    && token.token_type != TokenType::LParen; // no space before (
23128                if needs_space
23129                    && token.token_type != TokenType::RParen
23130                    && token.token_type != TokenType::Comma
23131                {
23132                    result.push(' ');
23133                }
23134                // Properly quote string literals
23135                if token.token_type == TokenType::String {
23136                    result.push('\'');
23137                    result.push_str(&token.text.replace('\'', "''"));
23138                    result.push('\'');
23139                } else {
23140                    result.push_str(&token.text);
23141                }
23142                if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma {
23143                    result.push(' ');
23144                }
23145                prev_token_type = Some(token.token_type);
23146            }
23147            result.push(')');
23148            Some(Expression::Raw(Raw { sql: result }))
23149        } else {
23150            None
23151        };
23152
23153        // ClickHouse: ON CLUSTER clause
23154        let _on_cluster = self.parse_on_cluster_clause()?;
23155
23156        let mut options = Vec::new();
23157
23158        // Parse database options
23159        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
23160            if self.match_identifier("OWNER") || self.match_token(TokenType::Eq) {
23161                self.match_token(TokenType::Eq);
23162                options.push(DatabaseOption::Owner(Identifier::new(
23163                    self.expect_identifier()?,
23164                )));
23165            } else if self.match_identifier("TEMPLATE") {
23166                self.match_token(TokenType::Eq);
23167                options.push(DatabaseOption::Template(Identifier::new(
23168                    self.expect_identifier()?,
23169                )));
23170            } else if self.match_identifier("ENCODING") {
23171                self.match_token(TokenType::Eq);
23172                let encoding = if self.check(TokenType::String) {
23173                    let tok = self.advance();
23174                    tok.text.trim_matches('\'').to_string()
23175                } else {
23176                    self.expect_identifier()?
23177                };
23178                options.push(DatabaseOption::Encoding(encoding));
23179            } else if self.match_identifier("CHARACTER") {
23180                self.match_token(TokenType::Set);
23181                self.match_token(TokenType::Eq);
23182                let charset = if self.check(TokenType::String) {
23183                    let tok = self.advance();
23184                    tok.text.trim_matches('\'').to_string()
23185                } else {
23186                    self.expect_identifier()?
23187                };
23188                options.push(DatabaseOption::CharacterSet(charset));
23189            } else if self.match_identifier("COLLATE") {
23190                self.match_token(TokenType::Eq);
23191                let collate = if self.check(TokenType::String) {
23192                    let tok = self.advance();
23193                    tok.text.trim_matches('\'').to_string()
23194                } else {
23195                    self.expect_identifier()?
23196                };
23197                options.push(DatabaseOption::Collate(collate));
23198            } else if self.match_identifier("LOCATION") {
23199                self.match_token(TokenType::Eq);
23200                let loc = if self.check(TokenType::String) {
23201                    let tok = self.advance();
23202                    tok.text.trim_matches('\'').to_string()
23203                } else {
23204                    self.expect_identifier()?
23205                };
23206                options.push(DatabaseOption::Location(loc));
23207            } else {
23208                break;
23209            }
23210        }
23211
23212        Ok(Expression::CreateDatabase(Box::new(CreateDatabase {
23213            name,
23214            if_not_exists,
23215            options,
23216            clone_from,
23217            at_clause,
23218        })))
23219    }
23220
23221    /// Parse DROP DATABASE statement
23222    fn parse_drop_database(&mut self) -> Result<Expression> {
23223        self.expect(TokenType::Database)?;
23224
23225        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23226
23227        // ClickHouse: IF EMPTY
23228        if !if_exists
23229            && matches!(
23230                self.config.dialect,
23231                Some(crate::dialects::DialectType::ClickHouse)
23232            )
23233        {
23234            if self.check(TokenType::If)
23235                && self.current + 1 < self.tokens.len()
23236                && self.tokens[self.current + 1]
23237                    .text
23238                    .eq_ignore_ascii_case("EMPTY")
23239            {
23240                self.skip(); // consume IF
23241                self.skip(); // consume EMPTY
23242            }
23243        }
23244        let name = Identifier::new(self.expect_identifier()?);
23245
23246        // ClickHouse: ON CLUSTER clause
23247        let sync = if matches!(
23248            self.config.dialect,
23249            Some(crate::dialects::DialectType::ClickHouse)
23250        ) {
23251            let _ = self.parse_on_cluster_clause()?;
23252            self.match_identifier("SYNC")
23253        } else {
23254            false
23255        };
23256
23257        Ok(Expression::DropDatabase(Box::new(DropDatabase {
23258            name,
23259            if_exists,
23260            sync,
23261        })))
23262    }
23263
23264    /// Parse CREATE FUNCTION statement
23265    fn parse_create_function(
23266        &mut self,
23267        or_replace: bool,
23268        or_alter: bool,
23269        temporary: bool,
23270        is_table_function: bool,
23271    ) -> Result<Expression> {
23272        self.expect(TokenType::Function)?;
23273
23274        let if_not_exists =
23275            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
23276        let name = self.parse_table_ref()?;
23277
23278        // Parse parameters (optional - some dialects allow CREATE FUNCTION f AS 'body')
23279        let (parameters, has_parens) = if self.match_token(TokenType::LParen) {
23280            let params = self.parse_function_parameters()?;
23281            self.expect(TokenType::RParen)?;
23282            (params, true)
23283        } else {
23284            (Vec::new(), false)
23285        };
23286
23287        // Track if LANGUAGE appears before RETURNS
23288        let mut language_first = false;
23289        let mut return_type = None;
23290        let mut language = None;
23291        let mut sql_data_access = None;
23292
23293        // Check for LANGUAGE before RETURNS
23294        if self.match_token(TokenType::Language) {
23295            language = Some(self.expect_identifier_or_keyword()?);
23296            language_first = true;
23297        }
23298
23299        // Parse RETURNS clause (may come before or after LANGUAGE)
23300        let mut returns_table_body: Option<String> = None;
23301        if self.match_token(TokenType::Returns) {
23302            if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
23303                // TSQL: RETURNS @var TABLE (col_defs)
23304                let var_name = self.advance().text.clone();
23305                if self.check(TokenType::Table) {
23306                    self.skip(); // consume TABLE
23307                    return_type = Some(DataType::Custom {
23308                        name: "TABLE".to_string(),
23309                    });
23310                    // Parse column definitions
23311                    if self.match_token(TokenType::LParen) {
23312                        let start = self.current;
23313                        let mut depth = 1;
23314                        while depth > 0 && !self.is_at_end() {
23315                            if self.check(TokenType::LParen) {
23316                                depth += 1;
23317                            }
23318                            if self.check(TokenType::RParen) {
23319                                depth -= 1;
23320                                if depth == 0 {
23321                                    break;
23322                                }
23323                            }
23324                            self.skip();
23325                        }
23326                        // Reconstruct the column definitions with proper spacing
23327                        let mut col_defs_str = String::new();
23328                        for (i, tok) in self.tokens[start..self.current].iter().enumerate() {
23329                            // Don't add space before comma, LParen, RParen
23330                            // Don't add space after LParen
23331                            let prev_tok = if i > 0 {
23332                                Some(&self.tokens[start + i - 1])
23333                            } else {
23334                                None
23335                            };
23336                            let needs_space = i > 0
23337                                && tok.token_type != TokenType::Comma
23338                                && tok.token_type != TokenType::RParen
23339                                && tok.token_type != TokenType::LParen
23340                                && prev_tok
23341                                    .map(|p| p.token_type != TokenType::LParen)
23342                                    .unwrap_or(true);
23343                            if needs_space {
23344                                col_defs_str.push(' ');
23345                            }
23346                            col_defs_str.push_str(&tok.text);
23347                        }
23348                        returns_table_body = Some(format!("{} TABLE ({})", var_name, col_defs_str));
23349                        self.expect(TokenType::RParen)?;
23350                    } else {
23351                        returns_table_body = Some(format!("{} TABLE", var_name));
23352                    }
23353                } else {
23354                    // Parse data type after var name
23355                    return_type = Some(self.parse_data_type()?);
23356                }
23357            } else if self.check(TokenType::Table) {
23358                // Could be:
23359                // - TSQL: RETURNS TABLE AS RETURN ...
23360                // - BigQuery: RETURNS TABLE <col1 TYPE, col2 TYPE>
23361                // - Snowflake: RETURNS TABLE(col1 TYPE, col2 TYPE)
23362                self.skip(); // consume TABLE
23363                if self.check(TokenType::Lt) {
23364                    // BigQuery: RETURNS TABLE <col1 TYPE, col2 TYPE>
23365                    self.skip(); // consume <
23366                    let mut cols = Vec::new();
23367                    loop {
23368                        let col_name = self.expect_identifier()?;
23369                        let col_type = self.parse_data_type()?;
23370                        cols.push(format!(
23371                            "{} {}",
23372                            col_name,
23373                            self.data_type_to_string(&col_type)
23374                        ));
23375                        if !self.match_token(TokenType::Comma) {
23376                            break;
23377                        }
23378                    }
23379                    if !self.match_token(TokenType::Gt) {
23380                        return Err(self.parse_error("Expected > after TABLE column definitions"));
23381                    }
23382                    returns_table_body = Some(format!("TABLE <{}>", cols.join(", ")));
23383                } else if self.check(TokenType::LParen) {
23384                    // Snowflake: RETURNS TABLE(col1 TYPE, col2 TYPE)
23385                    self.skip(); // consume (
23386                    let mut cols = Vec::new();
23387                    loop {
23388                        let col_name = self.expect_identifier()?;
23389                        let col_type = self.parse_data_type()?;
23390                        cols.push(format!(
23391                            "{} {}",
23392                            col_name,
23393                            self.data_type_to_string(&col_type)
23394                        ));
23395                        if !self.match_token(TokenType::Comma) {
23396                            break;
23397                        }
23398                    }
23399                    self.expect(TokenType::RParen)?;
23400                    returns_table_body = Some(format!("TABLE ({})", cols.join(", ")));
23401                } else {
23402                    // TSQL: RETURNS TABLE AS RETURN ...
23403                    return_type = Some(DataType::Custom {
23404                        name: "TABLE".to_string(),
23405                    });
23406                }
23407            } else {
23408                // Use parse_function_return_type to preserve original type names like 'integer'
23409                return_type = Some(self.parse_function_return_type()?);
23410            }
23411        }
23412
23413        let mut deterministic = None;
23414        let mut returns_null_on_null_input = None;
23415        let mut strict = false;
23416        let mut security = None;
23417        let mut body = None;
23418        let mut set_options: Vec<FunctionSetOption> = Vec::new();
23419        let mut property_order: Vec<FunctionPropertyKind> = Vec::new();
23420        let mut options: Vec<Expression> = Vec::new();
23421        let mut environment: Vec<Expression> = Vec::new();
23422        let mut handler: Option<String> = None;
23423        let mut parameter_style: Option<String> = None;
23424
23425        // Parse function options
23426        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
23427            if self.check(TokenType::Returns)
23428                && self.current + 1 < self.tokens.len()
23429                && self.tokens[self.current + 1].token_type == TokenType::Null
23430            {
23431                // RETURNS NULL ON NULL INPUT
23432                self.skip(); // consume RETURNS
23433                self.skip(); // consume NULL
23434                self.match_token(TokenType::On);
23435                self.match_token(TokenType::Null);
23436                self.match_token(TokenType::Input);
23437                returns_null_on_null_input = Some(true);
23438                if !property_order.contains(&FunctionPropertyKind::NullInput) {
23439                    property_order.push(FunctionPropertyKind::NullInput);
23440                }
23441            } else if self.match_token(TokenType::Returns) {
23442                // RETURNS can come after LANGUAGE
23443                return_type = Some(self.parse_data_type()?);
23444            } else if self.match_token(TokenType::Language) {
23445                // Language can be SQL, PLPGSQL, PYTHON, etc.
23446                language = Some(self.expect_identifier_or_keyword()?);
23447                if !property_order.contains(&FunctionPropertyKind::Language) {
23448                    property_order.push(FunctionPropertyKind::Language);
23449                }
23450            } else if self.match_token(TokenType::Not) && self.match_identifier("DETERMINISTIC") {
23451                deterministic = Some(false);
23452                if !property_order.contains(&FunctionPropertyKind::Determinism) {
23453                    property_order.push(FunctionPropertyKind::Determinism);
23454                }
23455            } else if self.match_identifier("DETERMINISTIC") {
23456                deterministic = Some(true);
23457                if !property_order.contains(&FunctionPropertyKind::Determinism) {
23458                    property_order.push(FunctionPropertyKind::Determinism);
23459                }
23460            } else if self.match_identifier("IMMUTABLE") {
23461                deterministic = Some(true);
23462                if !property_order.contains(&FunctionPropertyKind::Determinism) {
23463                    property_order.push(FunctionPropertyKind::Determinism);
23464                }
23465            } else if self.match_identifier("STABLE") || self.match_identifier("VOLATILE") {
23466                deterministic = Some(false);
23467                if !property_order.contains(&FunctionPropertyKind::Determinism) {
23468                    property_order.push(FunctionPropertyKind::Determinism);
23469                }
23470            } else if self.match_identifier("STRICT") {
23471                returns_null_on_null_input = Some(true);
23472                strict = true;
23473                if !property_order.contains(&FunctionPropertyKind::NullInput) {
23474                    property_order.push(FunctionPropertyKind::NullInput);
23475                }
23476            } else if self.match_identifier("CALLED") {
23477                self.match_token(TokenType::On);
23478                self.match_token(TokenType::Null);
23479                self.match_token(TokenType::Input);
23480                returns_null_on_null_input = Some(false);
23481                if !property_order.contains(&FunctionPropertyKind::NullInput) {
23482                    property_order.push(FunctionPropertyKind::NullInput);
23483                }
23484            } else if self.match_identifier("SECURITY") {
23485                if self.match_identifier("DEFINER") {
23486                    security = Some(FunctionSecurity::Definer);
23487                } else if self.match_identifier("INVOKER") {
23488                    security = Some(FunctionSecurity::Invoker);
23489                }
23490                if !property_order.contains(&FunctionPropertyKind::Security) {
23491                    property_order.push(FunctionPropertyKind::Security);
23492                }
23493            } else if self.match_identifier("CONTAINS") {
23494                // CONTAINS SQL
23495                self.match_identifier("SQL");
23496                sql_data_access = Some(SqlDataAccess::ContainsSql);
23497                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
23498                    property_order.push(FunctionPropertyKind::SqlDataAccess);
23499                }
23500            } else if self.match_identifier("READS") {
23501                // READS SQL DATA
23502                self.match_identifier("SQL");
23503                self.match_identifier("DATA");
23504                sql_data_access = Some(SqlDataAccess::ReadsSqlData);
23505                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
23506                    property_order.push(FunctionPropertyKind::SqlDataAccess);
23507                }
23508            } else if self.match_identifier("MODIFIES") {
23509                // MODIFIES SQL DATA
23510                self.match_identifier("SQL");
23511                self.match_identifier("DATA");
23512                sql_data_access = Some(SqlDataAccess::ModifiesSqlData);
23513                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
23514                    property_order.push(FunctionPropertyKind::SqlDataAccess);
23515                }
23516            } else if self.match_token(TokenType::No) && self.match_identifier("SQL") {
23517                // NO SQL
23518                sql_data_access = Some(SqlDataAccess::NoSql);
23519                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
23520                    property_order.push(FunctionPropertyKind::SqlDataAccess);
23521                }
23522            } else if self.match_token(TokenType::Set) {
23523                // PostgreSQL: SET key = value / SET key TO value / SET key FROM CURRENT
23524                let opt_name = self.expect_identifier_or_keyword()?;
23525                let value = if self.match_token(TokenType::From) {
23526                    // SET key FROM CURRENT
23527                    if !self.match_token(TokenType::Current) {
23528                        return Err(self.parse_error("Expected CURRENT after FROM in SET option"));
23529                    }
23530                    FunctionSetValue::FromCurrent
23531                } else {
23532                    // SET key = value or SET key TO value
23533                    let use_to = self.match_token(TokenType::To);
23534                    if !use_to && !self.match_token(TokenType::Eq) {
23535                        return Err(self.parse_error("Expected = or TO after SET key"));
23536                    }
23537                    // Value can be a string literal or identifier
23538                    let val = if self.check(TokenType::String) {
23539                        let tok = self.advance();
23540                        format!("'{}'", tok.text)
23541                    } else {
23542                        self.expect_identifier_or_keyword()?
23543                    };
23544                    FunctionSetValue::Value { value: val, use_to }
23545                };
23546                set_options.push(FunctionSetOption {
23547                    name: opt_name,
23548                    value,
23549                });
23550                if !property_order.contains(&FunctionPropertyKind::Set) {
23551                    property_order.push(FunctionPropertyKind::Set);
23552                }
23553            } else if self.match_token(TokenType::As) {
23554                // Parse function body: AS RETURN x, AS $$ ... $$, AS BEGIN ... END, AS 'body'
23555                if !property_order.contains(&FunctionPropertyKind::As) {
23556                    property_order.push(FunctionPropertyKind::As);
23557                }
23558                if self.match_identifier("RETURN") {
23559                    // AS RETURN expression (or SELECT statement for TSQL TVFs)
23560                    let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
23561                        // TSQL: AS RETURN SELECT ... for table-valued functions
23562                        self.parse_statement()?
23563                    } else {
23564                        self.parse_expression()?
23565                    };
23566                    body = Some(FunctionBody::Return(expr));
23567                } else if self.check(TokenType::Select) || self.check(TokenType::With) {
23568                    // TSQL: AS SELECT ... for table-valued functions (without RETURN keyword)
23569                    let stmt = self.parse_statement()?;
23570                    body = Some(FunctionBody::Expression(stmt));
23571                } else if self.check(TokenType::DollarString) {
23572                    let tok = self.advance();
23573                    // Parse the dollar string token to extract tag and content
23574                    let (tag, content) = crate::tokens::parse_dollar_string_token(&tok.text);
23575                    body = Some(FunctionBody::DollarQuoted { content, tag });
23576                } else if self.check(TokenType::String) {
23577                    let tok = self.advance();
23578                    body = Some(FunctionBody::StringLiteral(tok.text.clone()));
23579                } else if self.match_token(TokenType::Begin) {
23580                    // Parse BEGIN...END block
23581                    let mut block_content = String::new();
23582                    let mut depth = 1;
23583                    while depth > 0 && !self.is_at_end() {
23584                        let tok = self.advance();
23585                        if tok.token_type == TokenType::Begin {
23586                            depth += 1;
23587                        } else if tok.token_type == TokenType::End {
23588                            depth -= 1;
23589                            if depth == 0 {
23590                                break;
23591                            }
23592                        }
23593                        block_content.push_str(&tok.text);
23594                        block_content.push(' ');
23595                    }
23596                    body = Some(FunctionBody::Block(block_content.trim().to_string()));
23597                } else {
23598                    // Expression-based body
23599                    let expr = self.parse_expression()?;
23600                    body = Some(FunctionBody::Expression(expr));
23601                }
23602            } else if self.match_identifier("RETURN") {
23603                // RETURN expression (or SELECT statement for TSQL TVFs)
23604                let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
23605                    self.parse_statement()?
23606                } else {
23607                    self.parse_expression()?
23608                };
23609                body = Some(FunctionBody::Return(expr));
23610            } else if self.match_identifier("EXTERNAL") {
23611                self.match_identifier("NAME");
23612                let ext_name = if self.check(TokenType::String) {
23613                    let tok = self.advance();
23614                    tok.text.trim_matches('\'').to_string()
23615                } else {
23616                    self.expect_identifier()?
23617                };
23618                body = Some(FunctionBody::External(ext_name));
23619            } else if self.match_identifier("OPTIONS") {
23620                // BigQuery: OPTIONS (key=value, ...) - track in property_order
23621                let parsed_options = self.parse_options_list()?;
23622                options.extend(parsed_options);
23623                if !property_order.contains(&FunctionPropertyKind::Options) {
23624                    property_order.push(FunctionPropertyKind::Options);
23625                }
23626            } else if self.match_identifier("ENVIRONMENT") {
23627                // Databricks: ENVIRONMENT (dependencies = '...', environment_version = '...')
23628                let parsed_env = self.parse_environment_list()?;
23629                environment.extend(parsed_env);
23630                if !property_order.contains(&FunctionPropertyKind::Environment) {
23631                    property_order.push(FunctionPropertyKind::Environment);
23632                }
23633            } else if self.match_identifier("HANDLER") {
23634                // Databricks: HANDLER 'handler_function'
23635                if self.check(TokenType::String) {
23636                    let tok = self.advance();
23637                    handler = Some(tok.text.clone());
23638                }
23639                if !property_order.contains(&FunctionPropertyKind::Handler) {
23640                    property_order.push(FunctionPropertyKind::Handler);
23641                }
23642            } else if self.match_text_seq(&["PARAMETER", "STYLE"]) {
23643                // Databricks: PARAMETER STYLE PANDAS
23644                let style = self.expect_identifier_or_keyword()?;
23645                parameter_style = Some(style.to_ascii_uppercase());
23646                if !property_order.contains(&FunctionPropertyKind::ParameterStyle) {
23647                    property_order.push(FunctionPropertyKind::ParameterStyle);
23648                }
23649            } else if self.check_identifier("SQL")
23650                && self.current + 1 < self.tokens.len()
23651                && self.tokens[self.current + 1]
23652                    .text
23653                    .eq_ignore_ascii_case("SECURITY")
23654            {
23655                // SQL SECURITY DEFINER/INVOKER
23656                self.skip(); // consume SQL
23657                self.skip(); // consume SECURITY
23658                if self.match_identifier("DEFINER") {
23659                    security = Some(FunctionSecurity::Definer);
23660                } else if self.match_identifier("INVOKER") {
23661                    security = Some(FunctionSecurity::Invoker);
23662                }
23663                if !property_order.contains(&FunctionPropertyKind::Security) {
23664                    property_order.push(FunctionPropertyKind::Security);
23665                }
23666            } else if self.check(TokenType::Select) || self.check(TokenType::With) {
23667                // Bare SELECT/WITH body (without AS keyword) - e.g., MySQL
23668                let stmt = self.parse_statement()?;
23669                body = Some(FunctionBody::Expression(stmt));
23670                if !property_order.contains(&FunctionPropertyKind::As) {
23671                    property_order.push(FunctionPropertyKind::As);
23672                }
23673            } else {
23674                break;
23675            }
23676        }
23677
23678        // BigQuery: OPTIONS (key=value, ...) can also appear after AS body (legacy position)
23679        if options.is_empty() && self.match_identifier("OPTIONS") {
23680            let parsed_options = self.parse_options_list()?;
23681            options.extend(parsed_options);
23682            if !property_order.contains(&FunctionPropertyKind::Options) {
23683                property_order.push(FunctionPropertyKind::Options);
23684            }
23685        }
23686
23687        Ok(Expression::CreateFunction(Box::new(CreateFunction {
23688            name,
23689            parameters,
23690            return_type,
23691            body,
23692            or_replace,
23693            or_alter,
23694            if_not_exists,
23695            temporary,
23696            language,
23697            deterministic,
23698            returns_null_on_null_input,
23699            security,
23700            has_parens,
23701            sql_data_access,
23702            returns_table_body,
23703            language_first,
23704            set_options,
23705            strict,
23706            options,
23707            is_table_function,
23708            property_order,
23709            environment,
23710            handler,
23711            parameter_style,
23712        })))
23713    }
23714
23715    /// Parse function parameters
23716    fn parse_function_parameters(&mut self) -> Result<Vec<FunctionParameter>> {
23717        let mut params = Vec::new();
23718
23719        if self.check(TokenType::RParen) {
23720            return Ok(params);
23721        }
23722
23723        loop {
23724            let mut mode = None;
23725            let mut mode_text: Option<String> = None;
23726
23727            // Check for parameter mode (IN, OUT, INOUT, VARIADIC)
23728            // Note: OUT, INOUT, VARIADIC are tokenized as Var, not as dedicated keywords
23729            if self.match_token(TokenType::In) {
23730                // IN or IN OUT
23731                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OUT") {
23732                    let out_text = self.advance().text.clone(); // consume OUT
23733                    mode_text = Some(format!("IN {}", out_text));
23734                    mode = Some(ParameterMode::InOut);
23735                } else {
23736                    mode_text = Some("IN".to_string());
23737                    mode = Some(ParameterMode::In);
23738                }
23739            } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OUT") {
23740                let text = self.advance().text.clone();
23741                mode_text = Some(text);
23742                mode = Some(ParameterMode::Out);
23743            } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("INOUT") {
23744                let text = self.advance().text.clone();
23745                mode_text = Some(text);
23746                mode = Some(ParameterMode::InOut);
23747            } else if self.check(TokenType::Var)
23748                && self.peek().text.eq_ignore_ascii_case("VARIADIC")
23749            {
23750                let text = self.advance().text.clone();
23751                mode_text = Some(text);
23752                mode = Some(ParameterMode::Variadic);
23753            }
23754
23755            // Try to parse name and type
23756            // After a mode keyword (VARIADIC, OUT, etc.), the next thing could be:
23757            //   - a type directly (e.g., VARIADIC INT[], OUT INT)
23758            //   - a name then a type (e.g., VARIADIC a INT[], OUT result INT)
23759            //
23760            // Strategy: use backtracking. Save position, try parsing as data type.
23761            // If the result is followed by , or ) or DEFAULT, it was a type-only param.
23762            // Otherwise, restore position and parse as name + type.
23763            let (name, data_type) = if mode.is_some() {
23764                let saved = self.current;
23765                // Try parsing as a data type directly
23766                let type_result = self.parse_data_type();
23767                if let Ok(dt) = type_result {
23768                    if self.check(TokenType::Comma)
23769                        || self.check(TokenType::RParen)
23770                        || self.check(TokenType::Default)
23771                        || self.check(TokenType::Eq)
23772                    {
23773                        // Successfully parsed as a type-only parameter
23774                        (None, dt)
23775                    } else {
23776                        // Not followed by comma/rparen — restore and parse as name + type
23777                        self.current = saved;
23778                        let first_ident =
23779                            if self.check(TokenType::Input) || self.check(TokenType::Output) {
23780                                let token = self.advance();
23781                                Identifier {
23782                                    name: token.text,
23783                                    quoted: false,
23784                                    trailing_comments: Vec::new(),
23785                                    span: None,
23786                                }
23787                            } else {
23788                                self.expect_identifier_with_quoted()?
23789                            };
23790                        self.match_token(TokenType::As);
23791                        let dt = self.parse_data_type()?;
23792                        (Some(first_ident), dt)
23793                    }
23794                } else {
23795                    // Type parse failed — restore and try as name + type
23796                    self.current = saved;
23797                    let first_ident =
23798                        if self.check(TokenType::Input) || self.check(TokenType::Output) {
23799                            let token = self.advance();
23800                            Identifier {
23801                                name: token.text,
23802                                quoted: false,
23803                                trailing_comments: Vec::new(),
23804                                span: None,
23805                            }
23806                        } else {
23807                            self.expect_identifier_with_quoted()?
23808                        };
23809                    if self.check(TokenType::Comma)
23810                        || self.check(TokenType::RParen)
23811                        || self.check(TokenType::Default)
23812                    {
23813                        (None, self.identifier_to_datatype(&first_ident.name)?)
23814                    } else {
23815                        self.match_token(TokenType::As);
23816                        let dt = self.parse_data_type()?;
23817                        (Some(first_ident), dt)
23818                    }
23819                }
23820            } else {
23821                // No mode keyword — original logic
23822                // Handle keywords like INPUT that may be used as parameter names
23823                let first_ident = if self.check(TokenType::Input) || self.check(TokenType::Output) {
23824                    let token = self.advance();
23825                    Identifier {
23826                        name: token.text,
23827                        quoted: false,
23828                        trailing_comments: Vec::new(),
23829                        span: None,
23830                    }
23831                } else {
23832                    self.expect_identifier_with_quoted()?
23833                };
23834
23835                // Check if next token is a type or if this was the type
23836                if self.check(TokenType::Comma)
23837                    || self.check(TokenType::RParen)
23838                    || self.check(TokenType::Default)
23839                {
23840                    // This was the type, no name
23841                    (None, self.identifier_to_datatype(&first_ident.name)?)
23842                } else {
23843                    // This was the name, next is type
23844                    // TSQL allows: @param AS type (optional AS keyword)
23845                    self.match_token(TokenType::As);
23846                    let dt = self.parse_data_type()?;
23847                    (Some(first_ident), dt)
23848                }
23849            };
23850
23851            let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq)
23852            {
23853                Some(self.parse_expression()?)
23854            } else {
23855                None
23856            };
23857
23858            params.push(FunctionParameter {
23859                name,
23860                data_type,
23861                mode,
23862                default,
23863                mode_text: mode_text.clone(),
23864            });
23865
23866            if !self.match_token(TokenType::Comma) {
23867                break;
23868            }
23869        }
23870
23871        Ok(params)
23872    }
23873
23874    /// Parse TSQL-style unparenthesized procedure parameters
23875    /// Format: @param1 TYPE, @param2 TYPE, ... AS
23876    fn parse_tsql_procedure_params(&mut self) -> Result<Vec<FunctionParameter>> {
23877        let mut params = Vec::new();
23878        loop {
23879            if !self.check(TokenType::Var) {
23880                break;
23881            }
23882            let name = self.advance().text.clone();
23883            // Skip optional AS keyword between name and type
23884            self.match_token(TokenType::As);
23885            let data_type = self.parse_data_type()?;
23886            let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq)
23887            {
23888                Some(self.parse_expression()?)
23889            } else {
23890                None
23891            };
23892            params.push(FunctionParameter {
23893                name: Some(Identifier::new(name)),
23894                data_type,
23895                mode: None,
23896                default,
23897                mode_text: None,
23898            });
23899            if !self.match_token(TokenType::Comma) {
23900                break;
23901            }
23902        }
23903        Ok(params)
23904    }
23905
23906    /// Convert identifier to DataType for function parameters.
23907    /// Preserves the original identifier name to maintain exact type name as written.
23908    /// This matches Python sqlglot's behavior where function parameter types like 'integer'
23909    /// are stored as Identifiers rather than normalized DataTypes.
23910    fn identifier_to_datatype(&self, ident: &str) -> Result<DataType> {
23911        // Always use DataType::Custom to preserve the exact type name as written.
23912        // This is important for identity tests where e.g. 'integer' should not be normalized to 'INT'.
23913        Ok(DataType::Custom {
23914            name: ident.to_string(),
23915        })
23916    }
23917
23918    /// Parse a data type for function RETURNS clause, preserving original type names.
23919    /// For simple type names like 'integer', preserves the original name rather than
23920    /// normalizing to INT. This matches Python sqlglot's behavior.
23921    /// For MySQL, uses standard parse_data_type() to ensure proper type mapping (e.g., VARCHAR -> TEXT).
23922    fn parse_function_return_type(&mut self) -> Result<DataType> {
23923        // MySQL needs standard data type parsing for proper type mapping
23924        if matches!(
23925            self.config.dialect,
23926            Some(crate::dialects::DialectType::MySQL)
23927        ) {
23928            return self.parse_data_type();
23929        }
23930
23931        // Check if it's a simple identifier that could be a type name
23932        if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
23933            && !self.check_next(TokenType::LParen)  // Not a parameterized type like VARCHAR(10)
23934            && !self.check_next(TokenType::LBracket)
23935        // Not an array type
23936        {
23937            let type_name = self.advance().text.clone();
23938            // Check if the next token indicates we should use parse_data_type instead
23939            // For complex types, fall through to parse_data_type
23940            return Ok(DataType::Custom { name: type_name });
23941        }
23942
23943        // For complex types, use standard parsing
23944        self.parse_data_type()
23945    }
23946
23947    /// Parse DROP FUNCTION statement
23948    fn parse_drop_function(&mut self) -> Result<Expression> {
23949        self.expect(TokenType::Function)?;
23950
23951        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23952        let name = self.parse_table_ref()?;
23953
23954        // Optional parameter types for overloaded functions
23955        let parameters = if self.match_token(TokenType::LParen) {
23956            let mut types = Vec::new();
23957            if !self.check(TokenType::RParen) {
23958                loop {
23959                    types.push(self.parse_data_type()?);
23960                    if !self.match_token(TokenType::Comma) {
23961                        break;
23962                    }
23963                }
23964            }
23965            self.expect(TokenType::RParen)?;
23966            Some(types)
23967        } else {
23968            None
23969        };
23970
23971        let cascade = self.match_token(TokenType::Cascade);
23972        if !cascade {
23973            self.match_token(TokenType::Restrict);
23974        }
23975
23976        Ok(Expression::DropFunction(Box::new(DropFunction {
23977            name,
23978            parameters,
23979            if_exists,
23980            cascade,
23981        })))
23982    }
23983
23984    /// Parse CREATE PROCEDURE statement
23985    fn parse_create_procedure(&mut self, or_replace: bool, or_alter: bool) -> Result<Expression> {
23986        // Check if PROC shorthand was used before consuming the token
23987        let use_proc_keyword = self.peek().text.eq_ignore_ascii_case("PROC");
23988        self.expect(TokenType::Procedure)?;
23989
23990        let if_not_exists =
23991            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
23992        let name = self.parse_table_ref()?;
23993
23994        // Parse parameters (optional parentheses for TSQL)
23995        let (parameters, has_parens) = if self.match_token(TokenType::LParen) {
23996            let params = self.parse_function_parameters()?;
23997            self.expect(TokenType::RParen)?;
23998            (params, true)
23999        } else if self.check(TokenType::Var) && !self.check(TokenType::As) {
24000            // TSQL: CREATE PROCEDURE foo @a INTEGER, @b INTEGER AS ...
24001            // Parameters without parentheses
24002            let params = self.parse_tsql_procedure_params()?;
24003            (params, false)
24004        } else {
24005            (Vec::new(), false)
24006        };
24007
24008        let mut language = None;
24009        let mut security = None;
24010        let mut body = None;
24011        let mut return_type = None;
24012        let mut execute_as = None;
24013        let mut with_options: Vec<String> = Vec::new();
24014
24015        // Parse procedure options
24016        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24017            if self.match_token(TokenType::Returns) {
24018                // RETURNS type (Snowflake)
24019                return_type = Some(self.parse_data_type()?);
24020            } else if self.match_identifier("EXECUTE") || self.match_token(TokenType::Execute) {
24021                // EXECUTE AS CALLER/OWNER (Snowflake)
24022                if self.match_token(TokenType::As) {
24023                    if self.match_identifier("CALLER") {
24024                        execute_as = Some("CALLER".to_string());
24025                    } else if self.match_identifier("OWNER") {
24026                        execute_as = Some("OWNER".to_string());
24027                    } else if self.match_identifier("SELF") {
24028                        execute_as = Some("SELF".to_string());
24029                    }
24030                }
24031            } else if self.match_token(TokenType::Language) {
24032                // Language can be SQL, PLPGSQL, PYTHON, etc.
24033                language = Some(self.expect_identifier_or_keyword()?);
24034            } else if self.match_identifier("SECURITY") {
24035                if self.match_identifier("DEFINER") {
24036                    security = Some(FunctionSecurity::Definer);
24037                } else if self.match_identifier("INVOKER") {
24038                    security = Some(FunctionSecurity::Invoker);
24039                }
24040            } else if self.match_token(TokenType::With) {
24041                // TSQL: WITH option1, option2, ... AS body
24042                // Options: ENCRYPTION, RECOMPILE, SCHEMABINDING, NATIVE_COMPILATION,
24043                //          EXECUTE AS {OWNER|SELF|CALLER|'username'}
24044                loop {
24045                    if self.match_identifier("EXECUTE") || self.match_token(TokenType::Execute) {
24046                        // EXECUTE AS {OWNER|SELF|CALLER|'username'}
24047                        self.expect(TokenType::As)?;
24048                        if self.check(TokenType::String) {
24049                            let tok = self.advance();
24050                            with_options.push(format!("EXECUTE AS '{}'", tok.text));
24051                        } else {
24052                            let ident = self.expect_identifier_or_keyword()?;
24053                            with_options.push(format!("EXECUTE AS {}", ident.to_ascii_uppercase()));
24054                        }
24055                    } else {
24056                        let opt = self.expect_identifier_or_keyword()?;
24057                        with_options.push(opt.to_ascii_uppercase());
24058                    }
24059                    if !self.match_token(TokenType::Comma) {
24060                        break;
24061                    }
24062                }
24063            } else if self.match_token(TokenType::As) {
24064                // Parse procedure body
24065                if self.check(TokenType::String) {
24066                    // TokenType::String means single-quoted - tokenizer strips quotes
24067                    let tok = self.advance();
24068                    body = Some(FunctionBody::StringLiteral(tok.text.clone()));
24069                } else if self.match_token(TokenType::Begin) {
24070                    // Parse BEGIN ... END block as a list of statements
24071                    let mut statements = Vec::new();
24072                    while !self.check(TokenType::End) && !self.is_at_end() {
24073                        // Skip optional semicolons between statements
24074                        while self.match_token(TokenType::Semicolon) {}
24075                        if self.check(TokenType::End) {
24076                            break;
24077                        }
24078                        statements.push(self.parse_statement()?);
24079                        // Skip optional semicolon after statement
24080                        self.match_token(TokenType::Semicolon);
24081                    }
24082                    self.expect(TokenType::End)?;
24083                    body = Some(FunctionBody::Statements(statements));
24084                } else {
24085                    // TSQL: AS <statement> (e.g., AS SELECT 1)
24086                    let stmt = self.parse_statement()?;
24087                    body = Some(FunctionBody::Expression(stmt));
24088                }
24089            } else {
24090                break;
24091            }
24092        }
24093
24094        Ok(Expression::CreateProcedure(Box::new(CreateProcedure {
24095            name,
24096            parameters,
24097            body,
24098            or_replace,
24099            or_alter,
24100            if_not_exists,
24101            language,
24102            security,
24103            return_type,
24104            execute_as,
24105            with_options,
24106            has_parens,
24107            use_proc_keyword,
24108        })))
24109    }
24110
24111    /// Parse DROP PROCEDURE statement
24112    fn parse_drop_procedure(&mut self) -> Result<Expression> {
24113        self.expect(TokenType::Procedure)?;
24114
24115        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24116        let name = self.parse_table_ref()?;
24117
24118        let parameters = if self.match_token(TokenType::LParen) {
24119            let mut types = Vec::new();
24120            if !self.check(TokenType::RParen) {
24121                loop {
24122                    types.push(self.parse_data_type()?);
24123                    if !self.match_token(TokenType::Comma) {
24124                        break;
24125                    }
24126                }
24127            }
24128            self.expect(TokenType::RParen)?;
24129            Some(types)
24130        } else {
24131            None
24132        };
24133
24134        let cascade = self.match_token(TokenType::Cascade);
24135        if !cascade {
24136            self.match_token(TokenType::Restrict);
24137        }
24138
24139        Ok(Expression::DropProcedure(Box::new(DropProcedure {
24140            name,
24141            parameters,
24142            if_exists,
24143            cascade,
24144        })))
24145    }
24146
24147    /// Parse CREATE SEQUENCE statement
24148    fn parse_create_sequence(&mut self, temporary: bool, or_replace: bool) -> Result<Expression> {
24149        self.expect(TokenType::Sequence)?;
24150
24151        let if_not_exists =
24152            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24153        let name = self.parse_table_ref()?;
24154
24155        let mut seq = CreateSequence {
24156            name,
24157            if_not_exists,
24158            temporary,
24159            or_replace,
24160            as_type: None,
24161            increment: None,
24162            minvalue: None,
24163            maxvalue: None,
24164            start: None,
24165            cache: None,
24166            cycle: false,
24167            owned_by: None,
24168            owned_by_none: false,
24169            order: None,
24170            comment: None,
24171            sharing: None,
24172            scale_modifier: None,
24173            shard_modifier: None,
24174            property_order: Vec::new(),
24175        };
24176
24177        // Parse optional AS <type> clause (e.g., AS SMALLINT, AS BIGINT)
24178        if self.match_token(TokenType::As) {
24179            seq.as_type = Some(self.parse_data_type()?);
24180        }
24181
24182        // Parse sequence options
24183        // Handle optional WITH keyword before options (Snowflake: WITH START = n INCREMENT = n)
24184        self.match_token(TokenType::With);
24185
24186        loop {
24187            // Skip optional commas between options (Snowflake uses comma-separated options)
24188            self.match_token(TokenType::Comma);
24189
24190            if self.is_at_end() || self.check(TokenType::Semicolon) {
24191                break;
24192            }
24193
24194            if self.match_token(TokenType::Increment) || self.match_identifier("INCREMENT") {
24195                self.match_token(TokenType::By);
24196                self.match_token(TokenType::Eq); // Snowflake uses = instead of BY
24197                seq.increment = Some(self.parse_signed_integer()?);
24198                seq.property_order.push(SeqPropKind::Increment);
24199            } else if self.match_token(TokenType::Minvalue) {
24200                seq.minvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
24201                seq.property_order.push(SeqPropKind::Minvalue);
24202            } else if self.match_keywords(&[TokenType::No, TokenType::Minvalue]) {
24203                seq.minvalue = Some(SequenceBound::None);
24204                seq.property_order.push(SeqPropKind::Minvalue);
24205            } else if self.match_identifier("NOMINVALUE") {
24206                seq.minvalue = Some(SequenceBound::None);
24207                seq.property_order.push(SeqPropKind::NoMinvalueWord);
24208            } else if self.match_token(TokenType::Maxvalue) {
24209                seq.maxvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
24210                seq.property_order.push(SeqPropKind::Maxvalue);
24211            } else if self.match_keywords(&[TokenType::No, TokenType::Maxvalue]) {
24212                seq.maxvalue = Some(SequenceBound::None);
24213                seq.property_order.push(SeqPropKind::Maxvalue);
24214            } else if self.match_identifier("NOMAXVALUE") {
24215                seq.maxvalue = Some(SequenceBound::None);
24216                seq.property_order.push(SeqPropKind::NoMaxvalueWord);
24217            } else if self.match_token(TokenType::Start) {
24218                self.match_token(TokenType::With);
24219                self.match_token(TokenType::Eq); // Snowflake uses = instead of WITH
24220                seq.start = Some(self.parse_signed_integer()?);
24221                seq.property_order.push(SeqPropKind::Start);
24222            } else if self.match_token(TokenType::Cache) {
24223                seq.cache = Some(self.parse_signed_integer()?);
24224                seq.property_order.push(SeqPropKind::Cache);
24225            } else if self.match_identifier("NOCACHE") {
24226                // Oracle: NOCACHE (single word)
24227                seq.property_order.push(SeqPropKind::NoCacheWord);
24228            } else if self.match_token(TokenType::Cycle) {
24229                seq.cycle = true;
24230                seq.property_order.push(SeqPropKind::Cycle);
24231            } else if self.match_token(TokenType::NoCycle) {
24232                // NOCYCLE keyword token - preserve as single word
24233                seq.cycle = false;
24234                seq.property_order.push(SeqPropKind::NoCycleWord);
24235            } else if self.match_token(TokenType::No) {
24236                // Two-word NO forms
24237                if self.match_token(TokenType::Cycle) {
24238                    seq.cycle = false;
24239                    seq.property_order.push(SeqPropKind::NoCycle);
24240                } else if self.match_token(TokenType::Cache) || self.match_identifier("CACHE") {
24241                    seq.property_order.push(SeqPropKind::NoCache);
24242                } else if self.match_token(TokenType::Minvalue) {
24243                    seq.minvalue = Some(SequenceBound::None);
24244                    seq.property_order.push(SeqPropKind::Minvalue);
24245                } else if self.match_token(TokenType::Maxvalue) {
24246                    seq.maxvalue = Some(SequenceBound::None);
24247                    seq.property_order.push(SeqPropKind::Maxvalue);
24248                } else {
24249                    // Unexpected token after NO
24250                    break;
24251                }
24252            } else if self.match_token(TokenType::Owned) {
24253                self.expect(TokenType::By)?;
24254                if self.match_identifier("NONE") {
24255                    seq.owned_by = None;
24256                    seq.owned_by_none = true;
24257                } else {
24258                    seq.owned_by = Some(self.parse_table_ref()?);
24259                }
24260                seq.property_order.push(SeqPropKind::OwnedBy);
24261            } else if self.match_token(TokenType::Order) {
24262                // Snowflake/Oracle: ORDER option
24263                seq.order = Some(true);
24264                seq.property_order.push(SeqPropKind::Order);
24265            } else if self.match_identifier("NOORDER") {
24266                // Snowflake/Oracle: NOORDER option
24267                seq.order = Some(false);
24268                seq.property_order.push(SeqPropKind::NoOrder);
24269            } else if self.match_token(TokenType::Comment) || self.match_identifier("COMMENT") {
24270                // Snowflake: COMMENT = 'value'
24271                self.expect(TokenType::Eq)?;
24272                let comment_val = self.expect(TokenType::String)?;
24273                seq.comment = Some(comment_val.text.clone());
24274                seq.property_order.push(SeqPropKind::Comment);
24275            } else if self.match_identifier("SHARING") {
24276                // Oracle: SHARING=value
24277                self.expect(TokenType::Eq)?;
24278                let val = self.expect_identifier_or_keyword()?;
24279                seq.sharing = Some(val);
24280                seq.property_order.push(SeqPropKind::Sharing);
24281            } else if self.match_identifier("NOKEEP") {
24282                seq.property_order.push(SeqPropKind::NoKeep);
24283            } else if self.match_token(TokenType::Keep) || self.match_identifier("KEEP") {
24284                seq.property_order.push(SeqPropKind::Keep);
24285            } else if self.match_identifier("SCALE") {
24286                let modifier = if self.match_identifier("EXTEND") {
24287                    "EXTEND".to_string()
24288                } else if self.match_identifier("NOEXTEND") {
24289                    "NOEXTEND".to_string()
24290                } else {
24291                    String::new()
24292                };
24293                seq.scale_modifier = Some(modifier);
24294                seq.property_order.push(SeqPropKind::Scale);
24295            } else if self.match_identifier("NOSCALE") {
24296                seq.property_order.push(SeqPropKind::NoScale);
24297            } else if self.match_identifier("SHARD") {
24298                let modifier = if self.match_identifier("EXTEND") {
24299                    "EXTEND".to_string()
24300                } else if self.match_identifier("NOEXTEND") {
24301                    "NOEXTEND".to_string()
24302                } else {
24303                    String::new()
24304                };
24305                seq.shard_modifier = Some(modifier);
24306                seq.property_order.push(SeqPropKind::Shard);
24307            } else if self.match_identifier("NOSHARD") {
24308                seq.property_order.push(SeqPropKind::NoShard);
24309            } else if self.match_identifier("SESSION") {
24310                seq.property_order.push(SeqPropKind::Session);
24311            } else if self.match_identifier("GLOBAL") {
24312                seq.property_order.push(SeqPropKind::Global);
24313            } else {
24314                break;
24315            }
24316        }
24317
24318        Ok(Expression::CreateSequence(Box::new(seq)))
24319    }
24320
24321    /// Parse a signed integer (positive or negative)
24322    fn parse_signed_integer(&mut self) -> Result<i64> {
24323        let negative = self.match_token(TokenType::Dash);
24324        let tok = self.expect(TokenType::Number)?;
24325        let value: i64 = tok
24326            .text
24327            .parse()
24328            .map_err(|_| self.parse_error(format!("Invalid integer: {}", tok.text)))?;
24329        Ok(if negative { -value } else { value })
24330    }
24331
24332    /// Parse DROP SEQUENCE statement
24333    fn parse_drop_sequence(&mut self) -> Result<Expression> {
24334        self.expect(TokenType::Sequence)?;
24335
24336        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24337        let name = self.parse_table_ref()?;
24338
24339        let cascade = self.match_token(TokenType::Cascade);
24340        if !cascade {
24341            self.match_token(TokenType::Restrict);
24342        }
24343
24344        Ok(Expression::DropSequence(Box::new(DropSequence {
24345            name,
24346            if_exists,
24347            cascade,
24348        })))
24349    }
24350
24351    /// Parse ALTER SEQUENCE statement
24352    fn parse_alter_sequence(&mut self) -> Result<Expression> {
24353        self.expect(TokenType::Sequence)?;
24354
24355        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24356        let name = self.parse_table_ref()?;
24357
24358        let mut seq = AlterSequence {
24359            name,
24360            if_exists,
24361            increment: None,
24362            minvalue: None,
24363            maxvalue: None,
24364            start: None,
24365            restart: None,
24366            cache: None,
24367            cycle: None,
24368            owned_by: None,
24369        };
24370
24371        // Parse sequence options
24372        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24373            if self.match_token(TokenType::Increment) || self.match_identifier("INCREMENT") {
24374                self.match_token(TokenType::By);
24375                seq.increment = Some(self.parse_signed_integer()?);
24376            } else if self.match_token(TokenType::Minvalue) {
24377                seq.minvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
24378            } else if self.match_keywords(&[TokenType::No, TokenType::Minvalue]) {
24379                seq.minvalue = Some(SequenceBound::None);
24380            } else if self.match_token(TokenType::Maxvalue) {
24381                seq.maxvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
24382            } else if self.match_keywords(&[TokenType::No, TokenType::Maxvalue]) {
24383                seq.maxvalue = Some(SequenceBound::None);
24384            } else if self.match_token(TokenType::Start) {
24385                self.match_token(TokenType::With);
24386                seq.start = Some(self.parse_signed_integer()?);
24387            } else if self.match_token(TokenType::Restart) {
24388                if self.match_token(TokenType::With)
24389                    || self.check(TokenType::Number)
24390                    || self.check(TokenType::Dash)
24391                {
24392                    seq.restart = Some(Some(self.parse_signed_integer()?));
24393                } else {
24394                    seq.restart = Some(None);
24395                }
24396            } else if self.match_token(TokenType::Cache) {
24397                seq.cache = Some(self.parse_signed_integer()?);
24398            } else if self.match_token(TokenType::Cycle) {
24399                seq.cycle = Some(true);
24400            } else if self.match_token(TokenType::NoCycle) {
24401                seq.cycle = Some(false);
24402            } else if self.match_token(TokenType::Owned) {
24403                self.expect(TokenType::By)?;
24404                if self.match_identifier("NONE") {
24405                    seq.owned_by = Some(None);
24406                } else {
24407                    seq.owned_by = Some(Some(self.parse_table_ref()?));
24408                }
24409            } else {
24410                break;
24411            }
24412        }
24413
24414        Ok(Expression::AlterSequence(Box::new(seq)))
24415    }
24416
24417    /// Parse CREATE TRIGGER statement
24418    fn parse_create_trigger(
24419        &mut self,
24420        or_replace: bool,
24421        or_alter: bool,
24422        constraint: bool,
24423        create_pos: usize,
24424    ) -> Result<Expression> {
24425        self.expect(TokenType::Trigger)?;
24426
24427        let name = self.expect_identifier_with_quoted()?;
24428
24429        // TSQL triggers: CREATE TRIGGER name ON table AFTER INSERT AS BEGIN...END
24430        // These have ON before timing, unlike standard triggers.
24431        // Fall back to Command for these (matches Python sqlglot behavior).
24432        if self.check(TokenType::On) && !constraint {
24433            self.current = create_pos;
24434            return self.fallback_to_command(create_pos);
24435        }
24436
24437        // Parse timing (BEFORE, AFTER, INSTEAD OF)
24438        let timing = if self.match_token(TokenType::Before) {
24439            TriggerTiming::Before
24440        } else if self.match_token(TokenType::After) {
24441            TriggerTiming::After
24442        } else if self.match_token(TokenType::Instead) {
24443            self.expect(TokenType::Of)?;
24444            TriggerTiming::InsteadOf
24445        } else {
24446            // Fall back to Command for unknown trigger syntax
24447            self.current = create_pos;
24448            return self.fallback_to_command(create_pos);
24449        };
24450
24451        // Parse events
24452        let mut events = Vec::new();
24453        loop {
24454            if self.match_token(TokenType::Insert) {
24455                events.push(TriggerEvent::Insert);
24456            } else if self.match_token(TokenType::Update) {
24457                if self.match_token(TokenType::Of) {
24458                    let mut cols = Vec::new();
24459                    loop {
24460                        cols.push(Identifier::new(self.expect_identifier()?));
24461                        if !self.match_token(TokenType::Comma) {
24462                            break;
24463                        }
24464                    }
24465                    events.push(TriggerEvent::Update(Some(cols)));
24466                } else {
24467                    events.push(TriggerEvent::Update(None));
24468                }
24469            } else if self.match_token(TokenType::Delete) {
24470                events.push(TriggerEvent::Delete);
24471            } else if self.match_token(TokenType::Truncate) {
24472                events.push(TriggerEvent::Truncate);
24473            } else {
24474                break;
24475            }
24476
24477            if !self.match_token(TokenType::Or) {
24478                break;
24479            }
24480        }
24481
24482        self.expect(TokenType::On)?;
24483        let table = self.parse_table_ref()?;
24484
24485        // Parse optional REFERENCING clause (for non-constraint triggers)
24486        let referencing = if !constraint && self.match_token(TokenType::Referencing) {
24487            let mut ref_clause = TriggerReferencing {
24488                old_table: None,
24489                new_table: None,
24490                old_row: None,
24491                new_row: None,
24492            };
24493            while self.match_token(TokenType::Old) || self.match_token(TokenType::New) {
24494                let is_old = self.previous().token_type == TokenType::Old;
24495                let is_table = self.match_token(TokenType::Table);
24496                let _is_row = !is_table && self.match_token(TokenType::Row);
24497                self.match_token(TokenType::As);
24498                let alias = Identifier::new(self.expect_identifier()?);
24499
24500                if is_old {
24501                    if is_table {
24502                        ref_clause.old_table = Some(alias);
24503                    } else {
24504                        ref_clause.old_row = Some(alias);
24505                    }
24506                } else {
24507                    if is_table {
24508                        ref_clause.new_table = Some(alias);
24509                    } else {
24510                        ref_clause.new_row = Some(alias);
24511                    }
24512                }
24513            }
24514            Some(ref_clause)
24515        } else {
24516            None
24517        };
24518
24519        // Parse deferrable options for constraint triggers (comes before FOR EACH ROW in PostgreSQL)
24520        let mut deferrable = None;
24521        let mut initially_deferred = None;
24522        if constraint {
24523            if self.match_identifier("DEFERRABLE") {
24524                deferrable = Some(true);
24525            } else if self.match_keywords(&[TokenType::Not, TokenType::Identifier]) {
24526                // NOT DEFERRABLE
24527                deferrable = Some(false);
24528            }
24529            if self.match_identifier("INITIALLY") {
24530                if self.match_identifier("DEFERRED") {
24531                    initially_deferred = Some(true);
24532                } else if self.match_identifier("IMMEDIATE") {
24533                    initially_deferred = Some(false);
24534                }
24535            }
24536        }
24537
24538        // Parse FOR EACH ROW/STATEMENT (optional)
24539        let for_each = if self.match_token(TokenType::For) {
24540            self.match_token(TokenType::Each);
24541            if self.match_token(TokenType::Row) {
24542                Some(TriggerForEach::Row)
24543            } else if self.match_token(TokenType::Statement) {
24544                Some(TriggerForEach::Statement)
24545            } else {
24546                Some(TriggerForEach::Row)
24547            }
24548        } else {
24549            None
24550        };
24551
24552        // Parse optional WHEN clause (parentheses are optional, e.g. SQLite)
24553        let (when, when_paren) = if self.match_token(TokenType::When) {
24554            let has_paren = self.match_token(TokenType::LParen);
24555            let expr = self.parse_expression()?;
24556            if has_paren {
24557                self.expect(TokenType::RParen)?;
24558            }
24559            (Some(expr), has_paren)
24560        } else {
24561            (None, false)
24562        };
24563
24564        // Parse trigger body
24565        let body = if self.match_token(TokenType::Execute) {
24566            self.match_token(TokenType::Function);
24567            self.match_token(TokenType::Procedure);
24568            let func_name = self.parse_table_ref()?;
24569            self.expect(TokenType::LParen)?;
24570            let mut args = Vec::new();
24571            if !self.check(TokenType::RParen) {
24572                loop {
24573                    args.push(self.parse_expression()?);
24574                    if !self.match_token(TokenType::Comma) {
24575                        break;
24576                    }
24577                }
24578            }
24579            self.expect(TokenType::RParen)?;
24580            TriggerBody::Execute {
24581                function: func_name,
24582                args,
24583            }
24584        } else if self.match_token(TokenType::Begin) {
24585            // Record start position (first token after BEGIN)
24586            let body_start = if !self.is_at_end() {
24587                self.tokens[self.current].span.start
24588            } else {
24589                0
24590            };
24591            let mut depth = 1;
24592            while depth > 0 && !self.is_at_end() {
24593                let tok = self.advance();
24594                if tok.token_type == TokenType::Begin {
24595                    depth += 1;
24596                } else if tok.token_type == TokenType::End {
24597                    depth -= 1;
24598                    if depth == 0 {
24599                        break;
24600                    }
24601                }
24602            }
24603            // Extract verbatim text from source if available
24604            let block_content = if let Some(ref source) = self.source {
24605                // End position is the start of the END token
24606                let body_end = if self.current > 0 {
24607                    self.tokens[self.current - 1].span.start
24608                } else {
24609                    body_start
24610                };
24611                source[body_start..body_end].trim().to_string()
24612            } else {
24613                // Fallback: no source available
24614                String::new()
24615            };
24616            TriggerBody::Block(block_content)
24617        } else {
24618            return Err(self.parse_error("Expected EXECUTE or BEGIN in trigger body"));
24619        };
24620
24621        Ok(Expression::CreateTrigger(Box::new(CreateTrigger {
24622            name,
24623            table,
24624            timing,
24625            events,
24626            for_each,
24627            when,
24628            when_paren,
24629            body,
24630            or_replace,
24631            or_alter,
24632            constraint,
24633            deferrable,
24634            initially_deferred,
24635            referencing,
24636        })))
24637    }
24638
24639    /// Parse DROP TRIGGER statement
24640    fn parse_drop_trigger(&mut self) -> Result<Expression> {
24641        self.expect(TokenType::Trigger)?;
24642
24643        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24644        let name = Identifier::new(self.expect_identifier()?);
24645
24646        let table = if self.match_token(TokenType::On) {
24647            Some(self.parse_table_ref()?)
24648        } else {
24649            None
24650        };
24651
24652        let cascade = self.match_token(TokenType::Cascade);
24653        if !cascade {
24654            self.match_token(TokenType::Restrict);
24655        }
24656
24657        Ok(Expression::DropTrigger(Box::new(DropTrigger {
24658            name,
24659            table,
24660            if_exists,
24661            cascade,
24662        })))
24663    }
24664
24665    /// Parse CREATE TYPE statement
24666    fn parse_create_type(&mut self) -> Result<Expression> {
24667        self.expect(TokenType::Type)?;
24668
24669        let if_not_exists =
24670            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24671        let name = self.parse_table_ref()?;
24672
24673        self.expect(TokenType::As)?;
24674
24675        let definition = if self.match_token(TokenType::Enum) {
24676            // ENUM type
24677            self.expect(TokenType::LParen)?;
24678            let mut values = Vec::new();
24679            loop {
24680                let tok = self.expect(TokenType::String)?;
24681                values.push(tok.text.trim_matches('\'').to_string());
24682                if !self.match_token(TokenType::Comma) {
24683                    break;
24684                }
24685            }
24686            self.expect(TokenType::RParen)?;
24687            TypeDefinition::Enum(values)
24688        } else if self.match_token(TokenType::LParen) {
24689            // Composite type
24690            let mut attrs = Vec::new();
24691            loop {
24692                let attr_name = Identifier::new(self.expect_identifier()?);
24693                let data_type = self.parse_data_type()?;
24694                let collate = if self.match_identifier("COLLATE") {
24695                    Some(Identifier::new(self.expect_identifier()?))
24696                } else {
24697                    None
24698                };
24699                attrs.push(TypeAttribute {
24700                    name: attr_name,
24701                    data_type,
24702                    collate,
24703                });
24704                if !self.match_token(TokenType::Comma) {
24705                    break;
24706                }
24707            }
24708            self.expect(TokenType::RParen)?;
24709            TypeDefinition::Composite(attrs)
24710        } else if self.match_token(TokenType::Range) {
24711            // Range type
24712            self.expect(TokenType::LParen)?;
24713            self.match_identifier("SUBTYPE");
24714            self.match_token(TokenType::Eq);
24715            let subtype = self.parse_data_type()?;
24716
24717            let mut subtype_diff = None;
24718            let mut canonical = None;
24719
24720            while self.match_token(TokenType::Comma) {
24721                if self.match_identifier("SUBTYPE_DIFF") {
24722                    self.match_token(TokenType::Eq);
24723                    subtype_diff = Some(self.expect_identifier()?);
24724                } else if self.match_identifier("CANONICAL") {
24725                    self.match_token(TokenType::Eq);
24726                    canonical = Some(self.expect_identifier()?);
24727                }
24728            }
24729            self.expect(TokenType::RParen)?;
24730
24731            TypeDefinition::Range {
24732                subtype,
24733                subtype_diff,
24734                canonical,
24735            }
24736        } else {
24737            return Err(
24738                self.parse_error("Expected ENUM, composite type definition, or RANGE after AS")
24739            );
24740        };
24741
24742        Ok(Expression::CreateType(Box::new(CreateType {
24743            name,
24744            definition,
24745            if_not_exists,
24746        })))
24747    }
24748
24749    /// Parse CREATE DOMAIN statement
24750    fn parse_create_domain(&mut self) -> Result<Expression> {
24751        self.expect(TokenType::Domain)?;
24752
24753        let if_not_exists =
24754            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24755        let name = self.parse_table_ref()?;
24756
24757        self.expect(TokenType::As)?;
24758        let base_type = self.parse_data_type()?;
24759
24760        let mut default = None;
24761        let mut constraints = Vec::new();
24762
24763        // Parse domain options
24764        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24765            if self.match_token(TokenType::Default) {
24766                default = Some(self.parse_expression()?);
24767            } else if self.match_token(TokenType::Constraint) {
24768                let constr_name = Some(Identifier::new(self.expect_identifier()?));
24769                self.expect(TokenType::Check)?;
24770                self.expect(TokenType::LParen)?;
24771                let check_expr = self.parse_expression()?;
24772                self.expect(TokenType::RParen)?;
24773                constraints.push(DomainConstraint {
24774                    name: constr_name,
24775                    check: check_expr,
24776                });
24777            } else if self.match_token(TokenType::Check) {
24778                self.expect(TokenType::LParen)?;
24779                let check_expr = self.parse_expression()?;
24780                self.expect(TokenType::RParen)?;
24781                constraints.push(DomainConstraint {
24782                    name: None,
24783                    check: check_expr,
24784                });
24785            } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
24786                // NOT NULL is a constraint - represented as VALUE IS NOT NULL
24787                constraints.push(DomainConstraint {
24788                    name: None,
24789                    check: Expression::IsNull(Box::new(IsNull {
24790                        this: Expression::Identifier(Identifier::new("VALUE")),
24791                        not: true,
24792                        postfix_form: false,
24793                    })),
24794                });
24795            } else {
24796                break;
24797            }
24798        }
24799
24800        Ok(Expression::CreateType(Box::new(CreateType {
24801            name,
24802            definition: TypeDefinition::Domain {
24803                base_type,
24804                default,
24805                constraints,
24806            },
24807            if_not_exists,
24808        })))
24809    }
24810
24811    /// Parse CREATE STAGE statement (Snowflake)
24812    fn parse_create_stage(&mut self, or_replace: bool, temporary: bool) -> Result<Expression> {
24813        self.skip(); // consume STAGE (identifier)
24814                     // Parse remaining tokens, normalizing FILE_FORMAT clause
24815        let start = self.current;
24816        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24817            self.skip();
24818        }
24819        let sql = self.tokens_to_sql_stage_format(start, self.current);
24820
24821        // Build the CREATE prefix with modifiers
24822        let mut prefix = String::from("CREATE");
24823        if or_replace {
24824            prefix.push_str(" OR REPLACE");
24825        }
24826        if temporary {
24827            prefix.push_str(" TEMPORARY");
24828        }
24829        prefix.push_str(" STAGE");
24830
24831        Ok(Expression::Raw(Raw {
24832            sql: format!("{} {}", prefix, sql),
24833        }))
24834    }
24835
24836    /// Parse CREATE TAG statement (Snowflake)
24837    fn parse_create_tag(&mut self, or_replace: bool) -> Result<Expression> {
24838        self.skip(); // consume TAG
24839                     // Capture remaining tokens as raw SQL
24840        let start = self.current;
24841        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24842            self.skip();
24843        }
24844        let sql = self.tokens_to_sql(start, self.current);
24845        let prefix = if or_replace {
24846            "CREATE OR REPLACE TAG"
24847        } else {
24848            "CREATE TAG"
24849        };
24850        Ok(Expression::Raw(Raw {
24851            sql: format!("{} {}", prefix, sql),
24852        }))
24853    }
24854
24855    /// Parse CREATE STREAM statement (Snowflake)
24856    fn parse_create_stream(&mut self, _or_replace: bool) -> Result<Expression> {
24857        self.skip(); // consume STREAM
24858                     // Capture remaining tokens as raw SQL
24859        let start = self.current;
24860        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24861            self.skip();
24862        }
24863        let sql = self.tokens_to_sql(start, self.current);
24864        Ok(Expression::Raw(Raw {
24865            sql: format!("CREATE STREAM {}", sql),
24866        }))
24867    }
24868
24869    /// Parse CREATE TASK statement (Snowflake)
24870    /// CREATE [OR REPLACE] TASK [IF NOT EXISTS] name
24871    ///   [WAREHOUSE = wh] [SCHEDULE = '...'] [AFTER task1, ...] [WHEN expr]
24872    ///   AS sql_statement
24873    fn parse_create_task(&mut self, or_replace: bool) -> Result<Expression> {
24874        self.skip(); // consume TASK
24875
24876        let if_not_exists =
24877            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24878
24879        // Parse task name (possibly qualified: db.schema.task)
24880        let mut name = String::new();
24881        if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token() {
24882            name.push_str(&self.advance().text);
24883        }
24884        while self.check(TokenType::Dot) {
24885            self.skip();
24886            name.push('.');
24887            if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token() {
24888                name.push_str(&self.advance().text);
24889            }
24890        }
24891
24892        // Capture properties as raw text until AS keyword
24893        let props_start = self.current;
24894        while !self.is_at_end() && !self.check(TokenType::Semicolon) && !self.check(TokenType::As) {
24895            self.skip();
24896        }
24897        let properties = self.tokens_to_sql(props_start, self.current);
24898
24899        // Expect AS keyword followed by the SQL body
24900        if !self.match_token(TokenType::As) {
24901            return Err(self.parse_error("Expected AS keyword in CREATE TASK"));
24902        }
24903
24904        let body = self.parse_statement()?;
24905
24906        Ok(Expression::CreateTask(Box::new(
24907            crate::expressions::CreateTask {
24908                or_replace,
24909                if_not_exists,
24910                name,
24911                properties,
24912                body,
24913            },
24914        )))
24915    }
24916
24917    /// Parse CREATE FILE FORMAT statement (Snowflake)
24918    fn parse_create_file_format(
24919        &mut self,
24920        or_replace: bool,
24921        temporary: bool,
24922    ) -> Result<Expression> {
24923        self.skip(); // consume FILE
24924        self.skip(); // consume FORMAT
24925                     // Capture remaining tokens as raw SQL
24926        let start = self.current;
24927        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24928            self.skip();
24929        }
24930        let sql = self.tokens_to_sql(start, self.current);
24931        let mut prefix = String::from("CREATE");
24932        if or_replace {
24933            prefix.push_str(" OR REPLACE");
24934        }
24935        if temporary {
24936            prefix.push_str(" TEMPORARY");
24937        }
24938        prefix.push_str(" FILE FORMAT ");
24939        prefix.push_str(&sql);
24940        Ok(Expression::Raw(Raw { sql: prefix }))
24941    }
24942
24943    /// Parse DROP TYPE statement
24944    fn parse_drop_type(&mut self) -> Result<Expression> {
24945        self.expect(TokenType::Type)?;
24946
24947        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24948        let name = self.parse_table_ref()?;
24949
24950        let cascade = self.match_token(TokenType::Cascade);
24951        if !cascade {
24952            self.match_token(TokenType::Restrict);
24953        }
24954
24955        Ok(Expression::DropType(Box::new(DropType {
24956            name,
24957            if_exists,
24958            cascade,
24959        })))
24960    }
24961
24962    fn parse_alter_view_with_modifiers(
24963        &mut self,
24964        algorithm: Option<String>,
24965        definer: Option<String>,
24966        sql_security: Option<String>,
24967    ) -> Result<Expression> {
24968        self.expect(TokenType::View)?;
24969
24970        let name = self.parse_table_ref()?;
24971        let mut actions = Vec::new();
24972
24973        // Hive: Optional column aliases with optional COMMENT: (c1, c2) or (c1 COMMENT 'text', c2)
24974        // Only parse if we see LParen followed by identifier (not SELECT for subquery)
24975        let columns = if self.check(TokenType::LParen) {
24976            // Peek ahead to see if this looks like column aliases
24977            let saved = self.current;
24978            self.skip(); // consume LParen
24979
24980            // Check if this is an identifier (column name) vs SELECT keyword
24981            let is_column_aliases = self.check(TokenType::Identifier)
24982                || self.check(TokenType::Var)
24983                || self.check(TokenType::QuotedIdentifier);
24984
24985            if is_column_aliases {
24986                // Parse column aliases
24987                let mut cols = Vec::new();
24988                loop {
24989                    let col_name = self.expect_identifier()?;
24990                    // Optional COMMENT 'text'
24991                    let comment = if self.match_token(TokenType::Comment) {
24992                        Some(self.expect_string()?)
24993                    } else {
24994                        None
24995                    };
24996                    cols.push(ViewColumn {
24997                        name: Identifier::new(col_name),
24998                        comment,
24999                        options: Vec::new(),
25000                    });
25001                    if !self.match_token(TokenType::Comma) {
25002                        break;
25003                    }
25004                }
25005                self.expect(TokenType::RParen)?;
25006                cols
25007            } else {
25008                self.current = saved; // retreat
25009                Vec::new()
25010            }
25011        } else {
25012            Vec::new()
25013        };
25014
25015        // TSQL: WITH option (SCHEMABINDING, ENCRYPTION, VIEW_METADATA) before AS
25016        let with_option = if self.match_token(TokenType::With) {
25017            let opt = self.expect_identifier_or_keyword()?;
25018            Some(opt.to_ascii_uppercase())
25019        } else {
25020            None
25021        };
25022
25023        // Parse actions
25024        if self.match_token(TokenType::Rename) {
25025            self.expect(TokenType::To)?;
25026            actions.push(AlterViewAction::Rename(self.parse_table_ref()?));
25027        } else if self.match_identifier("OWNER") {
25028            self.expect(TokenType::To)?;
25029            actions.push(AlterViewAction::OwnerTo(Identifier::new(
25030                self.expect_identifier()?,
25031            )));
25032        } else if self.match_token(TokenType::Set) {
25033            // Hive: SET TBLPROPERTIES ('key'='value', ...) or SET SCHEMA name
25034            // Trino: SET AUTHORIZATION [ROLE] user
25035            if self.match_identifier("TBLPROPERTIES") {
25036                let props = self.parse_tblproperties_key_value_list()?;
25037                actions.push(AlterViewAction::SetTblproperties(props));
25038            } else if self.match_token(TokenType::Authorization) {
25039                let mut auth_text = String::new();
25040                if self.match_texts(&["ROLE"]) {
25041                    auth_text.push_str("ROLE ");
25042                }
25043                let user = self.expect_identifier()?;
25044                auth_text.push_str(&user);
25045                actions.push(AlterViewAction::SetAuthorization(auth_text));
25046            } else {
25047                self.expect(TokenType::Schema)?;
25048                actions.push(AlterViewAction::SetSchema(Identifier::new(
25049                    self.expect_identifier()?,
25050                )));
25051            }
25052        } else if self.match_identifier("UNSET") {
25053            // Hive: UNSET TBLPROPERTIES ('key1', 'key2', ...)
25054            if !self.match_identifier("TBLPROPERTIES") {
25055                return Err(self.parse_error("Expected TBLPROPERTIES after UNSET"));
25056            }
25057            let keys = self.parse_tblproperties_key_list()?;
25058            actions.push(AlterViewAction::UnsetTblproperties(keys));
25059        } else if self.match_token(TokenType::Alter) {
25060            self.match_token(TokenType::Column);
25061            let col_name = Identifier::new(self.expect_identifier()?);
25062            let action = self.parse_alter_column_action()?;
25063            actions.push(AlterViewAction::AlterColumn {
25064                name: col_name,
25065                action,
25066            });
25067        } else if self.match_token(TokenType::As) {
25068            // AS SELECT ... or AS SELECT ... UNION ... (redefine view query)
25069            let query = self.parse_statement()?;
25070            actions.push(AlterViewAction::AsSelect(Box::new(query)));
25071        }
25072
25073        Ok(Expression::AlterView(Box::new(AlterView {
25074            name,
25075            actions,
25076            algorithm,
25077            definer,
25078            sql_security,
25079            with_option,
25080            columns,
25081        })))
25082    }
25083
25084    /// Parse TBLPROPERTIES key-value list: ('key1'='value1', 'key2'='value2', ...)
25085    fn parse_tblproperties_key_value_list(&mut self) -> Result<Vec<(String, String)>> {
25086        self.expect(TokenType::LParen)?;
25087        let mut props = Vec::new();
25088        loop {
25089            let key = self.expect_string()?;
25090            self.expect(TokenType::Eq)?;
25091            let value = self.expect_string()?;
25092            props.push((key, value));
25093            if !self.match_token(TokenType::Comma) {
25094                break;
25095            }
25096        }
25097        self.expect(TokenType::RParen)?;
25098        Ok(props)
25099    }
25100
25101    /// Parse TBLPROPERTIES key list (for UNSET): ('key1', 'key2', ...)
25102    fn parse_tblproperties_key_list(&mut self) -> Result<Vec<String>> {
25103        self.expect(TokenType::LParen)?;
25104        let mut keys = Vec::new();
25105        loop {
25106            let key = self.expect_string()?;
25107            keys.push(key);
25108            if !self.match_token(TokenType::Comma) {
25109                break;
25110            }
25111        }
25112        self.expect(TokenType::RParen)?;
25113        Ok(keys)
25114    }
25115
25116    /// Parse ALTER INDEX statement
25117    fn parse_alter_index(&mut self) -> Result<Expression> {
25118        self.expect(TokenType::Index)?;
25119
25120        // Use expect_identifier_or_keyword_with_quoted to preserve quoted flag
25121        let name = self.expect_identifier_or_keyword_with_quoted()?;
25122
25123        let table = if self.match_token(TokenType::On) {
25124            Some(self.parse_table_ref()?)
25125        } else {
25126            None
25127        };
25128
25129        let mut actions = Vec::new();
25130
25131        // Parse actions
25132        if self.match_token(TokenType::Rename) {
25133            self.expect(TokenType::To)?;
25134            // Also preserve quoted flag for the new name
25135            actions.push(AlterIndexAction::Rename(
25136                self.expect_identifier_or_keyword_with_quoted()?,
25137            ));
25138        } else if self.match_token(TokenType::Set) {
25139            self.match_identifier("TABLESPACE");
25140            actions.push(AlterIndexAction::SetTablespace(
25141                self.expect_identifier_or_keyword_with_quoted()?,
25142            ));
25143        } else if self.match_identifier("VISIBLE") {
25144            actions.push(AlterIndexAction::Visible(true));
25145        } else if self.match_identifier("INVISIBLE") {
25146            actions.push(AlterIndexAction::Visible(false));
25147        }
25148
25149        Ok(Expression::AlterIndex(Box::new(AlterIndex {
25150            name,
25151            table,
25152            actions,
25153        })))
25154    }
25155
25156    // ==================== End DDL Parsing ====================
25157
25158    /// Parse an expression (with precedence)
25159    /// Assignment (:=) has lower precedence than OR, matching Python sqlglot's
25160    /// _parse_expression -> _parse_assignment -> _parse_disjunction chain
25161    fn parse_expression(&mut self) -> Result<Expression> {
25162        let mut left = self.parse_or()?;
25163
25164        // Handle := assignment operator (MySQL @var := val, DuckDB named args/settings)
25165        // This has lower precedence than OR
25166        while self.match_token(TokenType::ColonEq) {
25167            let right = self.parse_or()?;
25168            left = Expression::PropertyEQ(Box::new(BinaryOp::new(left, right)));
25169        }
25170
25171        // ClickHouse ternary operator: condition ? true_value : false_value
25172        // Parsed as: CASE WHEN condition THEN true_value ELSE false_value END
25173        if matches!(
25174            self.config.dialect,
25175            Some(crate::dialects::DialectType::ClickHouse)
25176        ) && self.match_token(TokenType::Parameter)
25177        {
25178            if self.check(TokenType::Colon) {
25179                return Err(
25180                    self.parse_error("Expected true expression after ? in ClickHouse ternary")
25181                );
25182            }
25183            let true_value = self.parse_or()?;
25184            let false_value = if self.match_token(TokenType::Colon) {
25185                self.parse_or()?
25186            } else {
25187                Expression::Null(Null)
25188            };
25189            left = Expression::IfFunc(Box::new(IfFunc {
25190                original_name: None,
25191                condition: left,
25192                true_value,
25193                false_value: Some(false_value),
25194                inferred_type: None,
25195            }));
25196        }
25197
25198        // ClickHouse: APPLY(func) column transformer
25199        // e.g., COLUMNS('pattern') APPLY(toString) APPLY(length)
25200        // Also: APPLY func (no parens), APPLY(x -> expr) (lambda)
25201        // Only match APPLY when followed by ( — bare APPLY without ( is treated as an alias
25202        // by the select expression parser (e.g., SELECT col apply -> SELECT col AS apply)
25203        if matches!(
25204            self.config.dialect,
25205            Some(crate::dialects::DialectType::ClickHouse)
25206        ) {
25207            while self.check(TokenType::Apply) && self.check_next(TokenType::LParen) {
25208                self.skip(); // consume APPLY
25209                self.skip(); // consume (
25210                let expr = self.parse_expression()?;
25211                self.expect(TokenType::RParen)?;
25212                left = Expression::Apply(Box::new(crate::expressions::Apply {
25213                    this: Box::new(left),
25214                    expression: Box::new(expr),
25215                }));
25216            }
25217        }
25218
25219        Ok(left)
25220    }
25221
25222    /// Parse OR expressions
25223    fn parse_or(&mut self) -> Result<Expression> {
25224        let mut left = self.parse_xor()?;
25225
25226        while self.check(TokenType::Or)
25227            || (self.dpipe_is_logical_or() && self.check(TokenType::DPipe))
25228        {
25229            let mut all_comments = self.previous_trailing_comments().to_vec();
25230            // Also capture leading comments on the OR token (comments on a separate line before OR)
25231            all_comments.extend_from_slice(self.current_leading_comments());
25232            self.skip(); // consume OR
25233            all_comments.extend_from_slice(self.previous_trailing_comments());
25234            // Clear trailing_comments from left expression to avoid duplication
25235            if !all_comments.is_empty() {
25236                Self::clear_rightmost_trailing_comments(&mut left);
25237            }
25238            // Filter out empty/whitespace-only comments
25239            all_comments.retain(|c| !c.trim().is_empty());
25240            // Split: block comments go before operator, line comments go after
25241            let mut left_comments = Vec::new();
25242            let mut operator_comments = Vec::new();
25243            for comment in all_comments {
25244                if comment.starts_with("/*") {
25245                    left_comments.push(comment);
25246                } else {
25247                    operator_comments.push(comment);
25248                }
25249            }
25250            let mut right = self.parse_xor()?;
25251            // If parse_comparison stored pending leading comments, attach them
25252            if !self.pending_leading_comments.is_empty() {
25253                let pending = std::mem::take(&mut self.pending_leading_comments);
25254                right = Expression::Annotated(Box::new(Annotated {
25255                    this: right,
25256                    trailing_comments: pending,
25257                }));
25258            }
25259            left = Expression::Or(Box::new(BinaryOp {
25260                left,
25261                right,
25262                left_comments,
25263                operator_comments,
25264                trailing_comments: Vec::new(),
25265                inferred_type: None,
25266            }));
25267        }
25268
25269        Ok(Self::maybe_rebalance_boolean_chain(left, false))
25270    }
25271
25272    /// Whether `||` should be parsed as logical OR for the active dialect.
25273    fn dpipe_is_logical_or(&self) -> bool {
25274        matches!(
25275            self.config.dialect,
25276            Some(crate::dialects::DialectType::MySQL | crate::dialects::DialectType::Solr)
25277        )
25278    }
25279
25280    /// Parse XOR expressions (MySQL logical XOR)
25281    fn parse_xor(&mut self) -> Result<Expression> {
25282        let mut left = self.parse_and()?;
25283
25284        while self.match_token(TokenType::Xor) {
25285            let right = self.parse_and()?;
25286            left = Expression::Xor(Box::new(Xor {
25287                this: Some(Box::new(left)),
25288                expression: Some(Box::new(right)),
25289                expressions: Vec::new(),
25290            }));
25291        }
25292
25293        Ok(left)
25294    }
25295
25296    /// Parse AND expressions
25297    fn parse_and(&mut self) -> Result<Expression> {
25298        let mut left = self.parse_not()?;
25299
25300        while self.check(TokenType::And) {
25301            // Capture comments from the token before AND (left operand's last token)
25302            let mut all_comments = self.previous_trailing_comments().to_vec();
25303            // Also capture leading comments on the AND token (comments on a separate line before AND)
25304            all_comments.extend_from_slice(self.current_leading_comments());
25305            self.skip(); // consume AND
25306                         // Also capture any trailing comments on the AND token itself
25307            all_comments.extend_from_slice(self.previous_trailing_comments());
25308            // Clear trailing_comments from left expression to avoid duplication
25309            if !all_comments.is_empty() {
25310                Self::clear_rightmost_trailing_comments(&mut left);
25311            }
25312            // Filter out empty/whitespace-only comments (e.g., bare "--" with no content)
25313            all_comments.retain(|c| !c.trim().is_empty());
25314            // Split comments: block comments (/*...*/) go BEFORE the operator (left_comments),
25315            // line comments (raw text from --) go AFTER the operator (operator_comments).
25316            // This matches Python sqlglot's behavior where inline block comments stay
25317            // in-place and line comments shift to after the operator.
25318            let mut left_comments = Vec::new();
25319            let mut operator_comments = Vec::new();
25320            for comment in all_comments {
25321                if comment.starts_with("/*") {
25322                    left_comments.push(comment);
25323                } else {
25324                    operator_comments.push(comment);
25325                }
25326            }
25327            let mut right = self.parse_not()?;
25328            // If parse_comparison stored pending leading comments (comments before
25329            // the right operand's first token with no comparison following),
25330            // attach them as trailing_comments on the right expression.
25331            if !self.pending_leading_comments.is_empty() {
25332                let pending = std::mem::take(&mut self.pending_leading_comments);
25333                right = Expression::Annotated(Box::new(Annotated {
25334                    this: right,
25335                    trailing_comments: pending,
25336                }));
25337            }
25338            left = Expression::And(Box::new(BinaryOp {
25339                left,
25340                right,
25341                left_comments,
25342                operator_comments,
25343                trailing_comments: Vec::new(),
25344                inferred_type: None,
25345            }));
25346        }
25347
25348        Ok(Self::maybe_rebalance_boolean_chain(left, true))
25349    }
25350
25351    /// Rebalance AND/OR chains into a balanced tree when no connector comments are present.
25352    /// This keeps connector chain depth logarithmic for very large predicates.
25353    fn maybe_rebalance_boolean_chain(expr: Expression, is_and: bool) -> Expression {
25354        if !Self::should_rebalance_boolean_chain(&expr, is_and) {
25355            return expr;
25356        }
25357
25358        let terms = Self::flatten_boolean_terms_owned(expr, is_and);
25359        if terms.len() <= 2 {
25360            return Self::build_balanced_boolean_tree(terms, is_and);
25361        }
25362
25363        Self::build_balanced_boolean_tree(terms, is_and)
25364    }
25365
25366    fn should_rebalance_boolean_chain(expr: &Expression, is_and: bool) -> bool {
25367        let mut leaf_count = 0usize;
25368        let mut stack = vec![expr];
25369
25370        while let Some(node) = stack.pop() {
25371            match (is_and, node) {
25372                (true, Expression::And(op)) => {
25373                    if !op.left_comments.is_empty()
25374                        || !op.operator_comments.is_empty()
25375                        || !op.trailing_comments.is_empty()
25376                    {
25377                        return false;
25378                    }
25379                    stack.push(&op.right);
25380                    stack.push(&op.left);
25381                }
25382                (false, Expression::Or(op)) => {
25383                    if !op.left_comments.is_empty()
25384                        || !op.operator_comments.is_empty()
25385                        || !op.trailing_comments.is_empty()
25386                    {
25387                        return false;
25388                    }
25389                    stack.push(&op.right);
25390                    stack.push(&op.left);
25391                }
25392                _ => leaf_count += 1,
25393            }
25394        }
25395
25396        leaf_count > 2
25397    }
25398
25399    fn flatten_boolean_terms_owned(expr: Expression, is_and: bool) -> Vec<Expression> {
25400        let mut terms = Vec::new();
25401        let mut stack = vec![expr];
25402
25403        while let Some(node) = stack.pop() {
25404            match (is_and, node) {
25405                (true, Expression::And(op)) => {
25406                    stack.push(op.right);
25407                    stack.push(op.left);
25408                }
25409                (false, Expression::Or(op)) => {
25410                    stack.push(op.right);
25411                    stack.push(op.left);
25412                }
25413                (_, other) => terms.push(other),
25414            }
25415        }
25416
25417        terms
25418    }
25419
25420    fn build_balanced_boolean_tree(mut terms: Vec<Expression>, is_and: bool) -> Expression {
25421        if terms.is_empty() {
25422            return Expression::Null(Null);
25423        }
25424
25425        while terms.len() > 1 {
25426            let mut next = Vec::with_capacity((terms.len() + 1) / 2);
25427            let mut iter = terms.into_iter();
25428
25429            while let Some(left) = iter.next() {
25430                if let Some(right) = iter.next() {
25431                    let combined = if is_and {
25432                        Expression::And(Box::new(BinaryOp::new(left, right)))
25433                    } else {
25434                        Expression::Or(Box::new(BinaryOp::new(left, right)))
25435                    };
25436                    next.push(combined);
25437                } else {
25438                    next.push(left);
25439                }
25440            }
25441
25442            terms = next;
25443        }
25444
25445        terms.pop().unwrap_or(Expression::Null(Null))
25446    }
25447
25448    /// Parse NOT expressions
25449    fn parse_not(&mut self) -> Result<Expression> {
25450        if self.match_token(TokenType::Not) {
25451            let expr = self.parse_not()?;
25452            Ok(Expression::Not(Box::new(UnaryOp::new(expr))))
25453        } else {
25454            self.parse_comparison()
25455        }
25456    }
25457
25458    /// Parse comparison expressions
25459    fn parse_comparison(&mut self) -> Result<Expression> {
25460        // Capture leading comments from the first token before parsing the left side.
25461        // If a comparison operator follows, these are placed after the left operand.
25462        let pre_left_comments = self.current_leading_comments().to_vec();
25463        let mut left = self.parse_bitwise_or()?;
25464
25465        // Only attach pre-left comments when a comparison operator follows.
25466        // When no comparison follows (e.g., in SELECT list expressions or AND operands),
25467        // the comments are returned to the caller by being accessible via the
25468        // `comparison_pre_left_comments` field, so they can be placed appropriately
25469        // (e.g., after an alias name, or after the expression in an AND chain).
25470        let has_comparison_op = !self.is_at_end()
25471            && matches!(
25472                self.peek().token_type,
25473                TokenType::Eq
25474                    | TokenType::Neq
25475                    | TokenType::Lt
25476                    | TokenType::Gt
25477                    | TokenType::Lte
25478                    | TokenType::Gte
25479                    | TokenType::Is
25480                    | TokenType::In
25481                    | TokenType::Not
25482                    | TokenType::Between
25483                    | TokenType::Like
25484                    | TokenType::ILike
25485                    | TokenType::RLike
25486                    | TokenType::SimilarTo
25487            );
25488
25489        if !pre_left_comments.is_empty() {
25490            if has_comparison_op {
25491                // Comparison follows: attach comments between left operand and operator
25492                match &mut left {
25493                    Expression::Column(col) => {
25494                        col.trailing_comments.extend(pre_left_comments);
25495                    }
25496                    Expression::Identifier(id) => {
25497                        id.trailing_comments.extend(pre_left_comments);
25498                    }
25499                    _ => {
25500                        left = Expression::Annotated(Box::new(Annotated {
25501                            this: left,
25502                            trailing_comments: pre_left_comments,
25503                        }));
25504                    }
25505                }
25506            } else {
25507                // No comparison operator: store comments for the caller to use.
25508                // Save them as "pending" comments that the caller can retrieve.
25509                self.pending_leading_comments = pre_left_comments;
25510            }
25511        }
25512
25513        loop {
25514            let mut global_in = false;
25515            if matches!(
25516                self.config.dialect,
25517                Some(crate::dialects::DialectType::ClickHouse)
25518            ) && self.check_identifier("GLOBAL")
25519                && (self.check_next(TokenType::Not) || self.check_next(TokenType::In))
25520            {
25521                self.skip();
25522                global_in = true;
25523            }
25524
25525            let expr = if self.match_token(TokenType::Eq) {
25526                // Check for ANY/ALL subquery
25527                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25528                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25529                    self.expect(TokenType::LParen)?;
25530                    let inner = self.parse_statement()?;
25531                    self.expect(TokenType::RParen)?;
25532                    let subquery = if was_any {
25533                        self.maybe_wrap_in_subquery(inner)
25534                    } else {
25535                        inner
25536                    };
25537                    Expression::Any(Box::new(QuantifiedExpr {
25538                        this: left,
25539                        subquery,
25540                        op: Some(QuantifiedOp::Eq),
25541                    }))
25542                } else if self.match_token(TokenType::All) {
25543                    self.expect(TokenType::LParen)?;
25544                    let inner = self.parse_statement()?;
25545                    self.expect(TokenType::RParen)?;
25546                    let subquery = self.maybe_wrap_in_subquery(inner);
25547                    Expression::All(Box::new(QuantifiedExpr {
25548                        this: left,
25549                        subquery,
25550                        op: Some(QuantifiedOp::Eq),
25551                    }))
25552                } else {
25553                    let right = self.parse_bitwise_or()?;
25554                    let trailing_comments = self.previous_trailing_comments().to_vec();
25555                    Expression::Eq(Box::new(BinaryOp {
25556                        left,
25557                        right,
25558                        left_comments: Vec::new(),
25559                        operator_comments: Vec::new(),
25560                        trailing_comments,
25561                        inferred_type: None,
25562                    }))
25563                }
25564            } else if self.match_token(TokenType::Neq) {
25565                // Check for ANY/ALL subquery
25566                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25567                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25568                    self.expect(TokenType::LParen)?;
25569                    let inner = self.parse_statement()?;
25570                    self.expect(TokenType::RParen)?;
25571                    let subquery = if was_any {
25572                        self.maybe_wrap_in_subquery(inner)
25573                    } else {
25574                        inner
25575                    };
25576                    Expression::Any(Box::new(QuantifiedExpr {
25577                        this: left,
25578                        subquery,
25579                        op: Some(QuantifiedOp::Neq),
25580                    }))
25581                } else if self.match_token(TokenType::All) {
25582                    self.expect(TokenType::LParen)?;
25583                    let inner = self.parse_statement()?;
25584                    self.expect(TokenType::RParen)?;
25585                    let subquery = self.maybe_wrap_in_subquery(inner);
25586                    Expression::All(Box::new(QuantifiedExpr {
25587                        this: left,
25588                        subquery,
25589                        op: Some(QuantifiedOp::Neq),
25590                    }))
25591                } else {
25592                    let right = self.parse_bitwise_or()?;
25593                    let trailing_comments = self.previous_trailing_comments().to_vec();
25594                    Expression::Neq(Box::new(BinaryOp {
25595                        left,
25596                        right,
25597                        left_comments: Vec::new(),
25598                        operator_comments: Vec::new(),
25599                        trailing_comments,
25600                        inferred_type: None,
25601                    }))
25602                }
25603            } else if self.match_token(TokenType::Lt) {
25604                // Check for ANY/ALL subquery
25605                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25606                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25607                    self.expect(TokenType::LParen)?;
25608                    let inner = self.parse_statement()?;
25609                    self.expect(TokenType::RParen)?;
25610                    let subquery = if was_any {
25611                        self.maybe_wrap_in_subquery(inner)
25612                    } else {
25613                        inner
25614                    };
25615                    Expression::Any(Box::new(QuantifiedExpr {
25616                        this: left,
25617                        subquery,
25618                        op: Some(QuantifiedOp::Lt),
25619                    }))
25620                } else if self.match_token(TokenType::All) {
25621                    self.expect(TokenType::LParen)?;
25622                    let inner = self.parse_statement()?;
25623                    self.expect(TokenType::RParen)?;
25624                    let subquery = self.maybe_wrap_in_subquery(inner);
25625                    Expression::All(Box::new(QuantifiedExpr {
25626                        this: left,
25627                        subquery,
25628                        op: Some(QuantifiedOp::Lt),
25629                    }))
25630                } else {
25631                    let right = self.parse_bitwise_or()?;
25632                    let trailing_comments = self.previous_trailing_comments().to_vec();
25633                    Expression::Lt(Box::new(BinaryOp {
25634                        left,
25635                        right,
25636                        left_comments: Vec::new(),
25637                        operator_comments: Vec::new(),
25638                        trailing_comments,
25639                        inferred_type: None,
25640                    }))
25641                }
25642            } else if self.match_token(TokenType::Lte) {
25643                // Check for ANY/ALL subquery
25644                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25645                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25646                    self.expect(TokenType::LParen)?;
25647                    let inner = self.parse_statement()?;
25648                    self.expect(TokenType::RParen)?;
25649                    let subquery = if was_any {
25650                        self.maybe_wrap_in_subquery(inner)
25651                    } else {
25652                        inner
25653                    };
25654                    Expression::Any(Box::new(QuantifiedExpr {
25655                        this: left,
25656                        subquery,
25657                        op: Some(QuantifiedOp::Lte),
25658                    }))
25659                } else if self.match_token(TokenType::All) {
25660                    self.expect(TokenType::LParen)?;
25661                    let inner = self.parse_statement()?;
25662                    self.expect(TokenType::RParen)?;
25663                    let subquery = self.maybe_wrap_in_subquery(inner);
25664                    Expression::All(Box::new(QuantifiedExpr {
25665                        this: left,
25666                        subquery,
25667                        op: Some(QuantifiedOp::Lte),
25668                    }))
25669                } else {
25670                    let right = self.parse_bitwise_or()?;
25671                    let trailing_comments = self.previous_trailing_comments().to_vec();
25672                    Expression::Lte(Box::new(BinaryOp {
25673                        left,
25674                        right,
25675                        left_comments: Vec::new(),
25676                        operator_comments: Vec::new(),
25677                        trailing_comments,
25678                        inferred_type: None,
25679                    }))
25680                }
25681            } else if self.match_token(TokenType::Gt) {
25682                // Check for ANY/ALL subquery
25683                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25684                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25685                    self.expect(TokenType::LParen)?;
25686                    let inner = self.parse_statement()?;
25687                    self.expect(TokenType::RParen)?;
25688                    let subquery = if was_any {
25689                        self.maybe_wrap_in_subquery(inner)
25690                    } else {
25691                        inner
25692                    };
25693                    Expression::Any(Box::new(QuantifiedExpr {
25694                        this: left,
25695                        subquery,
25696                        op: Some(QuantifiedOp::Gt),
25697                    }))
25698                } else if self.match_token(TokenType::All) {
25699                    self.expect(TokenType::LParen)?;
25700                    let inner = self.parse_statement()?;
25701                    self.expect(TokenType::RParen)?;
25702                    let subquery = self.maybe_wrap_in_subquery(inner);
25703                    Expression::All(Box::new(QuantifiedExpr {
25704                        this: left,
25705                        subquery,
25706                        op: Some(QuantifiedOp::Gt),
25707                    }))
25708                } else {
25709                    let right = self.parse_bitwise_or()?;
25710                    let trailing_comments = self.previous_trailing_comments().to_vec();
25711                    Expression::Gt(Box::new(BinaryOp {
25712                        left,
25713                        right,
25714                        left_comments: Vec::new(),
25715                        operator_comments: Vec::new(),
25716                        trailing_comments,
25717                        inferred_type: None,
25718                    }))
25719                }
25720            } else if self.match_token(TokenType::Gte) {
25721                // Check for ANY/ALL subquery
25722                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25723                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25724                    self.expect(TokenType::LParen)?;
25725                    let inner = self.parse_statement()?;
25726                    self.expect(TokenType::RParen)?;
25727                    let subquery = if was_any {
25728                        self.maybe_wrap_in_subquery(inner)
25729                    } else {
25730                        inner
25731                    };
25732                    Expression::Any(Box::new(QuantifiedExpr {
25733                        this: left,
25734                        subquery,
25735                        op: Some(QuantifiedOp::Gte),
25736                    }))
25737                } else if self.match_token(TokenType::All) {
25738                    self.expect(TokenType::LParen)?;
25739                    let inner = self.parse_statement()?;
25740                    self.expect(TokenType::RParen)?;
25741                    let subquery = self.maybe_wrap_in_subquery(inner);
25742                    Expression::All(Box::new(QuantifiedExpr {
25743                        this: left,
25744                        subquery,
25745                        op: Some(QuantifiedOp::Gte),
25746                    }))
25747                } else {
25748                    let right = self.parse_bitwise_or()?;
25749                    let trailing_comments = self.previous_trailing_comments().to_vec();
25750                    Expression::Gte(Box::new(BinaryOp {
25751                        left,
25752                        right,
25753                        left_comments: Vec::new(),
25754                        operator_comments: Vec::new(),
25755                        trailing_comments,
25756                        inferred_type: None,
25757                    }))
25758                }
25759            } else if self.match_token(TokenType::NullsafeEq) {
25760                // <=> (MySQL NULL-safe equality)
25761                let right = self.parse_bitwise_or()?;
25762                let trailing_comments = self.previous_trailing_comments().to_vec();
25763                Expression::NullSafeEq(Box::new(BinaryOp {
25764                    left,
25765                    right,
25766                    left_comments: Vec::new(),
25767                    operator_comments: Vec::new(),
25768                    trailing_comments,
25769                    inferred_type: None,
25770                }))
25771            } else if self.check_identifier("SOUNDS") && self.check_next(TokenType::Like) {
25772                // MySQL SOUNDS LIKE: expr SOUNDS LIKE expr -> SOUNDEX(expr) = SOUNDEX(expr)
25773                self.skip(); // consume SOUNDS
25774                self.skip(); // consume LIKE
25775                let right = self.parse_bitwise_or()?;
25776                // Transform: SOUNDEX(left) = SOUNDEX(right)
25777                let soundex_left = Expression::Function(Box::new(Function::new(
25778                    "SOUNDEX".to_string(),
25779                    vec![left],
25780                )));
25781                let soundex_right = Expression::Function(Box::new(Function::new(
25782                    "SOUNDEX".to_string(),
25783                    vec![right],
25784                )));
25785                Expression::Eq(Box::new(BinaryOp::new(soundex_left, soundex_right)))
25786            } else if self.match_token(TokenType::Like) {
25787                // Check for ANY/ALL/SOME quantifier
25788                let quantifier = if self.match_token(TokenType::Any) {
25789                    Some("ANY".to_string())
25790                } else if self.match_token(TokenType::All) {
25791                    Some("ALL".to_string())
25792                } else if self.match_token(TokenType::Some) {
25793                    Some("SOME".to_string())
25794                } else {
25795                    None
25796                };
25797                let right = self.parse_bitwise_or()?;
25798                let escape = if self.match_token(TokenType::Escape) {
25799                    Some(self.parse_primary()?)
25800                } else {
25801                    None
25802                };
25803                Expression::Like(Box::new(LikeOp {
25804                    left,
25805                    right,
25806                    escape,
25807                    quantifier,
25808                    inferred_type: None,
25809                }))
25810            } else if self.match_token(TokenType::ILike) {
25811                // Check for ANY/ALL/SOME quantifier
25812                let quantifier = if self.match_token(TokenType::Any) {
25813                    Some("ANY".to_string())
25814                } else if self.match_token(TokenType::All) {
25815                    Some("ALL".to_string())
25816                } else if self.match_token(TokenType::Some) {
25817                    Some("SOME".to_string())
25818                } else {
25819                    None
25820                };
25821                let right = self.parse_bitwise_or()?;
25822                let escape = if self.match_token(TokenType::Escape) {
25823                    Some(self.parse_primary()?)
25824                } else {
25825                    None
25826                };
25827                Expression::ILike(Box::new(LikeOp {
25828                    left,
25829                    right,
25830                    escape,
25831                    quantifier,
25832                    inferred_type: None,
25833                }))
25834            } else if self.check_identifier("SIMILAR") && self.check_next(TokenType::To) {
25835                // SIMILAR TO operator (PostgreSQL/Redshift regex-like pattern matching)
25836                self.skip(); // consume SIMILAR
25837                self.skip(); // consume TO
25838                let pattern = self.parse_bitwise_or()?;
25839                let escape = if self.match_token(TokenType::Escape) {
25840                    Some(self.parse_primary()?)
25841                } else {
25842                    None
25843                };
25844                Expression::SimilarTo(Box::new(SimilarToExpr {
25845                    this: left,
25846                    pattern,
25847                    escape,
25848                    not: false,
25849                }))
25850            } else if self.match_token(TokenType::Glob) {
25851                let right = self.parse_bitwise_or()?;
25852                Expression::Glob(Box::new(BinaryOp::new(left, right)))
25853            } else if self.match_token(TokenType::Match) {
25854                // SQLite MATCH operator (FTS full-text search)
25855                let right = self.parse_bitwise_or()?;
25856                Expression::Match(Box::new(BinaryOp::new(left, right)))
25857            } else if self.match_token(TokenType::RLike) || self.match_token(TokenType::Tilde) {
25858                // PostgreSQL ~ (regexp match) operator / RLIKE / REGEXP
25859                let right = self.parse_bitwise_or()?;
25860                Expression::RegexpLike(Box::new(RegexpFunc {
25861                    this: left,
25862                    pattern: right,
25863                    flags: None,
25864                }))
25865            } else if matches!(
25866                self.config.dialect,
25867                Some(crate::dialects::DialectType::Exasol)
25868            ) && self.check_identifier("REGEXP_LIKE")
25869            {
25870                // Exasol: REGEXP_LIKE as infix binary operator
25871                self.skip(); // consume REGEXP_LIKE
25872                let right = self.parse_bitwise_or()?;
25873                Expression::RegexpLike(Box::new(RegexpFunc {
25874                    this: left,
25875                    pattern: right,
25876                    flags: None,
25877                }))
25878            } else if self.match_token(TokenType::IRLike) {
25879                // PostgreSQL ~* (case-insensitive regexp match) operator
25880                let right = self.parse_bitwise_or()?;
25881                Expression::RegexpILike(Box::new(RegexpILike {
25882                    this: Box::new(left),
25883                    expression: Box::new(right),
25884                    flag: None,
25885                }))
25886            } else if self.match_token(TokenType::NotLike) {
25887                // PostgreSQL !~~ (NOT LIKE) operator
25888                let right = self.parse_bitwise_or()?;
25889                let escape = if self.match_token(TokenType::Escape) {
25890                    Some(self.parse_primary()?)
25891                } else {
25892                    None
25893                };
25894                let like_expr = Expression::Like(Box::new(LikeOp {
25895                    left,
25896                    right,
25897                    escape,
25898                    quantifier: None,
25899                    inferred_type: None,
25900                }));
25901                Expression::Not(Box::new(UnaryOp::new(like_expr)))
25902            } else if self.match_token(TokenType::NotILike) {
25903                // PostgreSQL !~~* (NOT ILIKE) operator
25904                let right = self.parse_bitwise_or()?;
25905                let escape = if self.match_token(TokenType::Escape) {
25906                    Some(self.parse_primary()?)
25907                } else {
25908                    None
25909                };
25910                let ilike_expr = Expression::ILike(Box::new(LikeOp {
25911                    left,
25912                    right,
25913                    escape,
25914                    quantifier: None,
25915                    inferred_type: None,
25916                }));
25917                Expression::Not(Box::new(UnaryOp::new(ilike_expr)))
25918            } else if self.match_token(TokenType::NotRLike) {
25919                // PostgreSQL !~ (NOT regexp match) operator
25920                let right = self.parse_bitwise_or()?;
25921                let regexp_expr = Expression::RegexpLike(Box::new(RegexpFunc {
25922                    this: left,
25923                    pattern: right,
25924                    flags: None,
25925                }));
25926                Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
25927            } else if self.match_token(TokenType::NotIRLike) {
25928                // PostgreSQL !~* (NOT case-insensitive regexp match) operator
25929                let right = self.parse_bitwise_or()?;
25930                let regexp_expr = Expression::RegexpILike(Box::new(RegexpILike {
25931                    this: Box::new(left),
25932                    expression: Box::new(right),
25933                    flag: None,
25934                }));
25935                Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
25936            } else if self.check(TokenType::Is)
25937                && !self.is_last_expression_token(TokenType::Is)
25938                && self.match_token(TokenType::Is)
25939            {
25940                let not = self.match_token(TokenType::Not);
25941                if self.match_token(TokenType::Null) {
25942                    let expr = Expression::IsNull(Box::new(IsNull {
25943                        this: left,
25944                        not,
25945                        postfix_form: false,
25946                    }));
25947                    // ClickHouse: IS NULL :: Type — handle :: cast after IS NULL
25948                    if matches!(
25949                        self.config.dialect,
25950                        Some(crate::dialects::DialectType::ClickHouse)
25951                    ) && self.check(TokenType::DColon)
25952                    {
25953                        self.skip(); // consume ::
25954                        let data_type = self.parse_data_type_for_cast()?;
25955                        Expression::Cast(Box::new(Cast {
25956                            this: expr,
25957                            to: data_type,
25958                            trailing_comments: Vec::new(),
25959                            double_colon_syntax: true,
25960                            format: None,
25961                            default: None,
25962                            inferred_type: None,
25963                        }))
25964                    } else {
25965                        expr
25966                    }
25967                } else if self.match_token(TokenType::True) {
25968                    // IS TRUE / IS NOT TRUE
25969                    Expression::IsTrue(Box::new(IsTrueFalse { this: left, not }))
25970                } else if self.match_token(TokenType::False) {
25971                    // IS FALSE / IS NOT FALSE
25972                    Expression::IsFalse(Box::new(IsTrueFalse { this: left, not }))
25973                } else if self.match_token(TokenType::Distinct) {
25974                    // IS DISTINCT FROM / IS NOT DISTINCT FROM
25975                    self.expect(TokenType::From)?;
25976                    let right = self.parse_bitwise_or()?;
25977                    if not {
25978                        // IS NOT DISTINCT FROM → null-safe equality
25979                        Expression::NullSafeEq(Box::new(BinaryOp::new(left, right)))
25980                    } else {
25981                        // IS DISTINCT FROM → null-safe inequality
25982                        Expression::NullSafeNeq(Box::new(BinaryOp::new(left, right)))
25983                    }
25984                } else if self.match_identifier("UNKNOWN") {
25985                    // IS UNKNOWN
25986                    Expression::IsNull(Box::new(IsNull {
25987                        this: left,
25988                        not,
25989                        postfix_form: false,
25990                    }))
25991                } else if self.match_texts(&["JSON"]) {
25992                    // IS JSON [VALUE|SCALAR|OBJECT|ARRAY] [WITH UNIQUE KEYS|WITHOUT UNIQUE KEYS|UNIQUE KEYS]
25993                    let json_type = if self.match_texts(&["VALUE"]) {
25994                        Some("VALUE".to_string())
25995                    } else if self.match_texts(&["SCALAR"]) {
25996                        Some("SCALAR".to_string())
25997                    } else if self.match_texts(&["OBJECT"]) {
25998                        Some("OBJECT".to_string())
25999                    } else if self.match_texts(&["ARRAY"]) {
26000                        Some("ARRAY".to_string())
26001                    } else {
26002                        None
26003                    };
26004
26005                    // Parse optional key uniqueness constraint
26006                    let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE", "KEYS"]) {
26007                        Some(JsonUniqueKeys::With)
26008                    } else if self.match_text_seq(&["WITHOUT", "UNIQUE", "KEYS"]) {
26009                        Some(JsonUniqueKeys::Without)
26010                    } else if self.match_text_seq(&["UNIQUE", "KEYS"]) {
26011                        // Shorthand for WITH UNIQUE KEYS
26012                        Some(JsonUniqueKeys::Shorthand)
26013                    } else {
26014                        None
26015                    };
26016
26017                    Expression::IsJson(Box::new(IsJson {
26018                        this: left,
26019                        json_type,
26020                        unique_keys,
26021                        negated: not,
26022                    }))
26023                } else {
26024                    // IS followed by an expression (e.g., IS ?)
26025                    // If we matched NOT, wrap the IS expression in NOT
26026                    let right = self.parse_primary()?;
26027                    let is_expr = Expression::Is(Box::new(BinaryOp::new(left, right)));
26028                    if not {
26029                        Expression::Not(Box::new(UnaryOp::new(is_expr)))
26030                    } else {
26031                        is_expr
26032                    }
26033                }
26034            } else if self.match_token(TokenType::Not) {
26035                // Handle NOT IN, NOT BETWEEN, NOT LIKE, NOT ILIKE, etc.
26036                if self.match_token(TokenType::In) {
26037                    // BigQuery: NOT IN UNNEST(expr)
26038                    if self.check_identifier("UNNEST") {
26039                        self.skip(); // consume UNNEST
26040                        self.expect(TokenType::LParen)?;
26041                        let unnest_expr = self.parse_expression()?;
26042                        self.expect(TokenType::RParen)?;
26043                        Expression::In(Box::new(In {
26044                            this: left,
26045                            expressions: Vec::new(),
26046                            query: None,
26047                            not: true,
26048                            global: global_in,
26049                            unnest: Some(Box::new(unnest_expr)),
26050                            is_field: false,
26051                        }))
26052                    } else if self.match_token(TokenType::LParen) {
26053                        if self.check(TokenType::Select) || self.check(TokenType::With) {
26054                            let subquery = self.parse_statement()?;
26055                            self.expect(TokenType::RParen)?;
26056                            Expression::In(Box::new(In {
26057                                this: left,
26058                                expressions: Vec::new(),
26059                                query: Some(subquery),
26060                                not: true,
26061                                global: global_in,
26062                                unnest: None,
26063                                is_field: false,
26064                            }))
26065                        } else if self.check(TokenType::RParen) {
26066                            // Empty NOT IN set: NOT IN ()
26067                            self.skip();
26068                            Expression::In(Box::new(In {
26069                                this: left,
26070                                expressions: Vec::new(),
26071                                query: None,
26072                                not: true,
26073                                global: global_in,
26074                                unnest: None,
26075                                is_field: false,
26076                            }))
26077                        } else {
26078                            let expressions = self.parse_expression_list()?;
26079                            self.expect(TokenType::RParen)?;
26080                            Expression::In(Box::new(In {
26081                                this: left,
26082                                expressions,
26083                                query: None,
26084                                not: true,
26085                                global: global_in,
26086                                unnest: None,
26087                                is_field: false,
26088                            }))
26089                        }
26090                    } else {
26091                        // ClickHouse/DuckDB: IN without parentheses: expr NOT IN table_name
26092                        let table_expr = self.parse_primary()?;
26093                        Expression::In(Box::new(In {
26094                            this: left,
26095                            expressions: vec![table_expr],
26096                            query: None,
26097                            not: true,
26098                            global: global_in,
26099                            unnest: None,
26100                            is_field: true,
26101                        }))
26102                    }
26103                } else if self.match_token(TokenType::Between) {
26104                    // Check for SYMMETRIC/ASYMMETRIC qualifier
26105                    let symmetric = if self.match_texts(&["SYMMETRIC"]) {
26106                        Some(true)
26107                    } else if self.match_texts(&["ASYMMETRIC"]) {
26108                        Some(false)
26109                    } else {
26110                        None
26111                    };
26112                    let low = self.parse_bitwise_or()?;
26113                    self.expect(TokenType::And)?;
26114                    let high = self.parse_bitwise_or()?;
26115                    Expression::Between(Box::new(Between {
26116                        this: left,
26117                        low,
26118                        high,
26119                        not: true,
26120                        symmetric,
26121                    }))
26122                } else if self.check_identifier("SOUNDS") && self.check_next(TokenType::Like) {
26123                    // MySQL NOT SOUNDS LIKE: expr NOT SOUNDS LIKE expr -> NOT SOUNDEX(expr) = SOUNDEX(expr)
26124                    self.skip(); // consume SOUNDS
26125                    self.skip(); // consume LIKE
26126                    let right = self.parse_bitwise_or()?;
26127                    let soundex_left = Expression::Function(Box::new(Function::new(
26128                        "SOUNDEX".to_string(),
26129                        vec![left],
26130                    )));
26131                    let soundex_right = Expression::Function(Box::new(Function::new(
26132                        "SOUNDEX".to_string(),
26133                        vec![right],
26134                    )));
26135                    let eq_expr =
26136                        Expression::Eq(Box::new(BinaryOp::new(soundex_left, soundex_right)));
26137                    Expression::Not(Box::new(UnaryOp::new(eq_expr)))
26138                } else if self.match_token(TokenType::Like) {
26139                    let right = self.parse_bitwise_or()?;
26140                    let escape = if self.match_token(TokenType::Escape) {
26141                        Some(self.parse_primary()?)
26142                    } else {
26143                        None
26144                    };
26145                    let like_expr = Expression::Like(Box::new(LikeOp {
26146                        left,
26147                        right,
26148                        escape,
26149                        quantifier: None,
26150                        inferred_type: None,
26151                    }));
26152                    Expression::Not(Box::new(UnaryOp::new(like_expr)))
26153                } else if self.match_token(TokenType::ILike) {
26154                    let right = self.parse_bitwise_or()?;
26155                    let escape = if self.match_token(TokenType::Escape) {
26156                        Some(self.parse_primary()?)
26157                    } else {
26158                        None
26159                    };
26160                    let ilike_expr = Expression::ILike(Box::new(LikeOp {
26161                        left,
26162                        right,
26163                        escape,
26164                        quantifier: None,
26165                        inferred_type: None,
26166                    }));
26167                    Expression::Not(Box::new(UnaryOp::new(ilike_expr)))
26168                } else if self.check_identifier("SIMILAR") && self.check_next(TokenType::To) {
26169                    // NOT SIMILAR TO
26170                    self.skip(); // consume SIMILAR
26171                    self.skip(); // consume TO
26172                    let pattern = self.parse_bitwise_or()?;
26173                    let escape = if self.match_token(TokenType::Escape) {
26174                        Some(self.parse_primary()?)
26175                    } else {
26176                        None
26177                    };
26178                    Expression::SimilarTo(Box::new(SimilarToExpr {
26179                        this: left,
26180                        pattern,
26181                        escape,
26182                        not: true,
26183                    }))
26184                } else if self.match_token(TokenType::RLike) {
26185                    let right = self.parse_bitwise_or()?;
26186                    let regexp_expr = Expression::RegexpLike(Box::new(RegexpFunc {
26187                        this: left,
26188                        pattern: right,
26189                        flags: None,
26190                    }));
26191                    Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
26192                } else if self.match_token(TokenType::Null) {
26193                    // SQLite: a NOT NULL (postfix form, two separate tokens)
26194                    // Creates NOT(a IS NULL) which is semantically equivalent
26195                    let is_null =
26196                        Expression::Is(Box::new(BinaryOp::new(left, Expression::Null(Null))));
26197                    Expression::Not(Box::new(UnaryOp::new(is_null)))
26198                } else {
26199                    // NOT followed by something else - revert
26200                    return Ok(left);
26201                }
26202            } else if self.match_token(TokenType::In) {
26203                // BigQuery: IN UNNEST(expr)
26204                if self.check_identifier("UNNEST") {
26205                    self.skip(); // consume UNNEST
26206                    self.expect(TokenType::LParen)?;
26207                    let unnest_expr = self.parse_expression()?;
26208                    self.expect(TokenType::RParen)?;
26209                    Expression::In(Box::new(In {
26210                        this: left,
26211                        expressions: Vec::new(),
26212                        query: None,
26213                        not: false,
26214                        global: global_in,
26215                        unnest: Some(Box::new(unnest_expr)),
26216                        is_field: false,
26217                    }))
26218                } else if self.match_token(TokenType::LParen) {
26219                    // Standard IN (list) or IN (subquery)
26220                    // Check if this is a subquery (IN (SELECT ...) or IN (WITH ... SELECT ...))
26221                    if self.check(TokenType::Select) || self.check(TokenType::With) {
26222                        // Use parse_statement to handle both SELECT and WITH...SELECT
26223                        let subquery = self.parse_statement()?;
26224                        self.expect(TokenType::RParen)?;
26225                        Expression::In(Box::new(In {
26226                            this: left,
26227                            expressions: Vec::new(),
26228                            query: Some(subquery),
26229                            not: false,
26230                            global: global_in,
26231                            unnest: None,
26232                            is_field: false,
26233                        }))
26234                    } else if self.check(TokenType::RParen) {
26235                        // Empty IN set: IN ()
26236                        self.skip();
26237                        Expression::In(Box::new(In {
26238                            this: left,
26239                            expressions: Vec::new(),
26240                            query: None,
26241                            not: false,
26242                            global: global_in,
26243                            unnest: None,
26244                            is_field: false,
26245                        }))
26246                    } else {
26247                        let expressions = self.parse_expression_list()?;
26248                        self.expect(TokenType::RParen)?;
26249                        Expression::In(Box::new(In {
26250                            this: left,
26251                            expressions,
26252                            query: None,
26253                            not: false,
26254                            global: global_in,
26255                            unnest: None,
26256                            is_field: false,
26257                        }))
26258                    }
26259                } else {
26260                    // DuckDB: IN without parentheses for array/list membership: 'red' IN tbl.flags
26261                    let expr = self.parse_bitwise_or()?;
26262                    Expression::In(Box::new(In {
26263                        this: left,
26264                        expressions: vec![expr],
26265                        query: None,
26266                        not: false,
26267                        global: global_in,
26268                        unnest: None,
26269                        is_field: true,
26270                    }))
26271                }
26272            } else if self.match_token(TokenType::Between) {
26273                // Check for SYMMETRIC/ASYMMETRIC qualifier
26274                let symmetric = if self.match_texts(&["SYMMETRIC"]) {
26275                    Some(true)
26276                } else if self.match_texts(&["ASYMMETRIC"]) {
26277                    Some(false)
26278                } else {
26279                    None
26280                };
26281                let low = self.parse_bitwise_or()?;
26282                self.expect(TokenType::And)?;
26283                let high = self.parse_bitwise_or()?;
26284                Expression::Between(Box::new(Between {
26285                    this: left,
26286                    low,
26287                    high,
26288                    not: false,
26289                    symmetric,
26290                }))
26291            } else if self.match_token(TokenType::Adjacent) {
26292                let right = self.parse_bitwise_or()?;
26293                Expression::Adjacent(Box::new(BinaryOp::new(left, right)))
26294            } else if self.check(TokenType::Overlaps)
26295                && self.current + 1 < self.tokens.len()
26296                && !matches!(
26297                    self.tokens[self.current + 1].token_type,
26298                    TokenType::Semicolon
26299                        | TokenType::Comma
26300                        | TokenType::From
26301                        | TokenType::Where
26302                        | TokenType::RParen
26303                        | TokenType::As
26304                        | TokenType::Join
26305                        | TokenType::On
26306                        | TokenType::OrderBy
26307                        | TokenType::GroupBy
26308                        | TokenType::Having
26309                        | TokenType::Limit
26310                        | TokenType::Union
26311                        | TokenType::Except
26312                        | TokenType::Intersect
26313                        | TokenType::Eof
26314                )
26315            {
26316                self.skip(); // consume OVERLAPS
26317                let right = self.parse_bitwise_or()?;
26318                Expression::Overlaps(Box::new(OverlapsExpr {
26319                    this: Some(left),
26320                    expression: Some(right),
26321                    left_start: None,
26322                    left_end: None,
26323                    right_start: None,
26324                    right_end: None,
26325                }))
26326            } else if self.match_token(TokenType::IsNull) {
26327                // ISNULL postfix operator (PostgreSQL/SQLite)
26328                Expression::IsNull(Box::new(IsNull {
26329                    this: left,
26330                    not: false,
26331                    postfix_form: true,
26332                }))
26333            } else if self.match_token(TokenType::NotNull) {
26334                // NOTNULL postfix operator (PostgreSQL/SQLite)
26335                Expression::IsNull(Box::new(IsNull {
26336                    this: left,
26337                    not: true,
26338                    postfix_form: true,
26339                }))
26340            } else if self.match_token(TokenType::AtAt) {
26341                // PostgreSQL text search match operator (@@)
26342                let right = self.parse_bitwise_or()?;
26343                Expression::TsMatch(Box::new(BinaryOp::new(left, right)))
26344            } else if self.match_token(TokenType::AtGt) {
26345                // PostgreSQL array contains all operator (@>)
26346                let right = self.parse_bitwise_or()?;
26347                Expression::ArrayContainsAll(Box::new(BinaryOp::new(left, right)))
26348            } else if self.match_token(TokenType::LtAt) {
26349                // PostgreSQL array contained by operator (<@)
26350                let right = self.parse_bitwise_or()?;
26351                Expression::ArrayContainedBy(Box::new(BinaryOp::new(left, right)))
26352            } else if self.match_token(TokenType::DAmp) {
26353                // PostgreSQL array overlaps operator (&&)
26354                let right = self.parse_bitwise_or()?;
26355                Expression::ArrayOverlaps(Box::new(BinaryOp::new(left, right)))
26356            } else if self.match_token(TokenType::QMarkAmp) {
26357                // PostgreSQL JSONB contains all top keys operator (?&)
26358                let right = self.parse_bitwise_or()?;
26359                Expression::JSONBContainsAllTopKeys(Box::new(BinaryOp::new(left, right)))
26360            } else if self.match_token(TokenType::QMarkPipe) {
26361                // PostgreSQL JSONB contains any top key operator (?|)
26362                let right = self.parse_bitwise_or()?;
26363                Expression::JSONBContainsAnyTopKeys(Box::new(BinaryOp::new(left, right)))
26364            } else if !matches!(
26365                self.config.dialect,
26366                Some(crate::dialects::DialectType::ClickHouse)
26367            ) && self.match_token(TokenType::Parameter)
26368            {
26369                // PostgreSQL JSONB contains key operator (?)
26370                // Note: ? is tokenized as Parameter, but when used between expressions
26371                // it's the JSONB key existence operator
26372                // ClickHouse uses ? as ternary operator instead, handled in parse_assignment()
26373                let right = self.parse_bitwise_or()?;
26374                Expression::JSONBContains(Box::new(BinaryFunc {
26375                    original_name: Some("?".to_string()),
26376                    this: left,
26377                    expression: right,
26378                    inferred_type: None,
26379                }))
26380            } else if self.match_token(TokenType::HashDash) {
26381                // PostgreSQL JSONB delete at path operator (#-)
26382                let right = self.parse_bitwise_or()?;
26383                Expression::JSONBDeleteAtPath(Box::new(BinaryOp::new(left, right)))
26384            } else if self.match_token(TokenType::AmpLt) {
26385                // PostgreSQL range extends left operator (&<)
26386                let right = self.parse_bitwise_or()?;
26387                Expression::ExtendsLeft(Box::new(BinaryOp::new(left, right)))
26388            } else if self.match_token(TokenType::AmpGt) {
26389                // PostgreSQL range extends right operator (&>)
26390                let right = self.parse_bitwise_or()?;
26391                Expression::ExtendsRight(Box::new(BinaryOp::new(left, right)))
26392            } else if self.match_identifier("MEMBER") {
26393                // MySQL MEMBER OF(expr) operator - JSON membership test
26394                self.expect(TokenType::Of)?;
26395                self.expect(TokenType::LParen)?;
26396                let right = self.parse_expression()?;
26397                self.expect(TokenType::RParen)?;
26398                Expression::MemberOf(Box::new(BinaryOp::new(left, right)))
26399            } else if self.match_token(TokenType::CaretAt) {
26400                // DuckDB/PostgreSQL starts-with operator (^@)
26401                let right = self.parse_bitwise_or()?;
26402                Expression::StartsWith(Box::new(BinaryFunc {
26403                    original_name: Some("^@".to_string()),
26404                    this: left,
26405                    expression: right,
26406                    inferred_type: None,
26407                }))
26408            } else if self.match_token(TokenType::LrArrow) {
26409                // PostgreSQL distance operator (<->)
26410                let right = self.parse_bitwise_or()?;
26411                Expression::EuclideanDistance(Box::new(EuclideanDistance {
26412                    this: Box::new(left),
26413                    expression: Box::new(right),
26414                }))
26415            } else if self.match_token(TokenType::Operator) {
26416                // PostgreSQL OPERATOR(schema.op) syntax for schema-qualified operators
26417                // Example: col1 OPERATOR(pg_catalog.~) col2
26418                self.expect(TokenType::LParen)?;
26419
26420                // Collect all tokens between parentheses as the operator text
26421                // This can include schema names, dots, and operator symbols like ~
26422                let mut op_text = String::new();
26423                while !self.check(TokenType::RParen) && !self.is_at_end() {
26424                    op_text.push_str(&self.peek().text);
26425                    self.skip();
26426                }
26427                self.expect(TokenType::RParen)?;
26428
26429                // Collect any inline comments (e.g., /* foo */) between OPERATOR() and the RHS
26430                // Try trailing comments of the RParen (previous token) first,
26431                // then leading comments of the next token
26432                let mut comments = if self.current > 0 {
26433                    std::mem::take(&mut self.tokens[self.current - 1].trailing_comments)
26434                } else {
26435                    Vec::new()
26436                };
26437                if comments.is_empty() && !self.is_at_end() {
26438                    comments = std::mem::take(&mut self.tokens[self.current].comments);
26439                }
26440
26441                // Parse the right-hand side expression
26442                let right = self.parse_bitwise_or()?;
26443
26444                Expression::Operator(Box::new(Operator {
26445                    this: Box::new(left),
26446                    operator: Some(Box::new(Expression::Identifier(Identifier::new(op_text)))),
26447                    expression: Box::new(right),
26448                    comments,
26449                }))
26450            } else {
26451                return Ok(left);
26452            };
26453
26454            left = expr;
26455        }
26456    }
26457
26458    /// Parse bitwise OR expressions (|)
26459    fn parse_bitwise_or(&mut self) -> Result<Expression> {
26460        let mut left = self.parse_bitwise_xor()?;
26461
26462        loop {
26463            if self.match_token(TokenType::Pipe) {
26464                let right = self.parse_bitwise_xor()?;
26465                left = Expression::BitwiseOr(Box::new(BinaryOp::new(left, right)));
26466            } else {
26467                return Ok(left);
26468            }
26469        }
26470    }
26471
26472    /// Parse bitwise operators with an existing left expression
26473    /// Used for DuckDB's @ operator when @col is tokenized as a single Var token
26474    /// We already have the column, now need to continue parsing any binary operators
26475    /// Follows the same precedence chain: bitwise -> shift -> addition -> multiplication
26476    fn parse_bitwise_continuation(&mut self, left: Expression) -> Result<Expression> {
26477        // Start from multiplication level since we have a primary expression (col)
26478        // Then work up through addition, shift, bitwise AND/XOR/OR
26479        let mult_result = self.parse_multiplication_continuation(left)?;
26480        let add_result = self.parse_addition_continuation(mult_result)?;
26481        self.parse_bitwise_or_continuation(add_result)
26482    }
26483
26484    /// Parse bitwise OR with an existing left expression
26485    fn parse_bitwise_or_continuation(&mut self, mut left: Expression) -> Result<Expression> {
26486        loop {
26487            if self.match_token(TokenType::Pipe) {
26488                let right = self.parse_bitwise_xor()?;
26489                left = Expression::BitwiseOr(Box::new(BinaryOp::new(left, right)));
26490            } else {
26491                return Ok(left);
26492            }
26493        }
26494    }
26495
26496    /// Parse multiplication/division with an existing left expression
26497    fn parse_multiplication_continuation(&mut self, mut left: Expression) -> Result<Expression> {
26498        loop {
26499            let expr = if self.match_token(TokenType::Star) {
26500                let right = self.parse_power()?;
26501                Expression::Mul(Box::new(BinaryOp::new(left, right)))
26502            } else if self.match_token(TokenType::Slash) {
26503                let right = self.parse_power()?;
26504                Expression::Div(Box::new(BinaryOp::new(left, right)))
26505            } else if self.match_token(TokenType::Percent) {
26506                let right = self.parse_power()?;
26507                Expression::Mod(Box::new(BinaryOp::new(left, right)))
26508            } else if !self.check(TokenType::QuotedIdentifier)
26509                && (self.match_identifier("DIV") || self.match_token(TokenType::Div))
26510            {
26511                // DIV keyword for integer division (Hive/Spark/MySQL/ClickHouse)
26512                // Don't match QuotedIdentifier — `DIV` is an identifier alias, not an operator
26513                // If DIV was matched as a Var (not keyword Div token), verify it's actually
26514                // an operator by checking that a right operand follows. Otherwise it's an alias.
26515                let matched_as_var = self.previous().token_type == TokenType::Var;
26516                if matched_as_var
26517                    && (self.is_at_end()
26518                        || self.check(TokenType::Semicolon)
26519                        || self.check(TokenType::From)
26520                        || self.check(TokenType::Where)
26521                        || self.check(TokenType::Comma)
26522                        || self.check(TokenType::RParen))
26523                {
26524                    // Backtrack: DIV is being used as an alias, not an operator
26525                    self.current -= 1;
26526                    return Ok(left);
26527                }
26528                let right = self.parse_power()?;
26529                Expression::IntDiv(Box::new(crate::expressions::BinaryFunc {
26530                    this: left,
26531                    expression: right,
26532                    original_name: None,
26533                    inferred_type: None,
26534                }))
26535            } else {
26536                return Ok(left);
26537            };
26538            left = expr;
26539        }
26540    }
26541
26542    /// Parse addition/subtraction with an existing left expression
26543    fn parse_addition_continuation(&mut self, mut left: Expression) -> Result<Expression> {
26544        loop {
26545            let left_comments = self.previous_trailing_comments().to_vec();
26546
26547            let expr = if self.match_token(TokenType::Plus) {
26548                let operator_comments = self.previous_trailing_comments().to_vec();
26549                let right = self.parse_at_time_zone()?;
26550                let trailing_comments = self.previous_trailing_comments().to_vec();
26551                Expression::Add(Box::new(BinaryOp {
26552                    left,
26553                    right,
26554                    left_comments,
26555                    operator_comments,
26556                    trailing_comments,
26557                    inferred_type: None,
26558                }))
26559            } else if self.match_token(TokenType::Dash) {
26560                let operator_comments = self.previous_trailing_comments().to_vec();
26561                let right = self.parse_at_time_zone()?;
26562                let trailing_comments = self.previous_trailing_comments().to_vec();
26563                Expression::Sub(Box::new(BinaryOp {
26564                    left,
26565                    right,
26566                    left_comments,
26567                    operator_comments,
26568                    trailing_comments,
26569                    inferred_type: None,
26570                }))
26571            } else if !self.dpipe_is_logical_or() && self.match_token(TokenType::DPipe) {
26572                let operator_comments = self.previous_trailing_comments().to_vec();
26573                let right = self.parse_at_time_zone()?;
26574                let trailing_comments = self.previous_trailing_comments().to_vec();
26575                Expression::Concat(Box::new(BinaryOp {
26576                    left,
26577                    right,
26578                    left_comments,
26579                    operator_comments,
26580                    trailing_comments,
26581                    inferred_type: None,
26582                }))
26583            } else if self.match_token(TokenType::DQMark) {
26584                let right = self.parse_at_time_zone()?;
26585                Expression::Coalesce(Box::new(crate::expressions::VarArgFunc {
26586                    expressions: vec![left, right],
26587                    original_name: None,
26588                    inferred_type: None,
26589                }))
26590            } else {
26591                return Ok(left);
26592            };
26593
26594            left = expr;
26595        }
26596    }
26597
26598    /// Parse bitwise XOR expressions (^)
26599    fn parse_bitwise_xor(&mut self) -> Result<Expression> {
26600        let mut left = self.parse_bitwise_and()?;
26601
26602        loop {
26603            // In PostgreSQL, ^ is POWER (handled at parse_power level), and # is BitwiseXor
26604            if matches!(
26605                self.config.dialect,
26606                Some(crate::dialects::DialectType::PostgreSQL)
26607                    | Some(crate::dialects::DialectType::Redshift)
26608            ) {
26609                if self.match_token(TokenType::Hash) {
26610                    let right = self.parse_bitwise_and()?;
26611                    left = Expression::BitwiseXor(Box::new(BinaryOp::new(left, right)));
26612                } else {
26613                    return Ok(left);
26614                }
26615            } else if self.match_token(TokenType::Caret) {
26616                let right = self.parse_bitwise_and()?;
26617                left = Expression::BitwiseXor(Box::new(BinaryOp::new(left, right)));
26618            } else {
26619                return Ok(left);
26620            }
26621        }
26622    }
26623
26624    /// Parse bitwise AND expressions (&)
26625    fn parse_bitwise_and(&mut self) -> Result<Expression> {
26626        let mut left = self.parse_shift()?;
26627
26628        loop {
26629            if self.match_token(TokenType::Amp) {
26630                let right = self.parse_shift()?;
26631                left = Expression::BitwiseAnd(Box::new(BinaryOp::new(left, right)));
26632            } else {
26633                return Ok(left);
26634            }
26635        }
26636    }
26637
26638    /// Parse shift expressions (<< and >>)
26639    fn parse_shift(&mut self) -> Result<Expression> {
26640        let mut left = self.parse_addition()?;
26641
26642        loop {
26643            if self.match_token(TokenType::LtLt) {
26644                let right = self.parse_addition()?;
26645                left = Expression::BitwiseLeftShift(Box::new(BinaryOp::new(left, right)));
26646            } else if self.match_token(TokenType::GtGt) {
26647                let right = self.parse_addition()?;
26648                left = Expression::BitwiseRightShift(Box::new(BinaryOp::new(left, right)));
26649            } else {
26650                return Ok(left);
26651            }
26652        }
26653    }
26654
26655    /// Parse addition/subtraction
26656    fn parse_addition(&mut self) -> Result<Expression> {
26657        let mut left = self.parse_at_time_zone()?;
26658
26659        loop {
26660            // Capture comments after left operand before consuming operator
26661            let left_comments = self.previous_trailing_comments().to_vec();
26662
26663            let expr = if self.match_token(TokenType::Plus) {
26664                // Capture comments after operator (before right operand)
26665                let operator_comments = self.previous_trailing_comments().to_vec();
26666                let right = self.parse_at_time_zone()?;
26667                let trailing_comments = self.previous_trailing_comments().to_vec();
26668                Expression::Add(Box::new(BinaryOp {
26669                    left,
26670                    right,
26671                    left_comments,
26672                    operator_comments,
26673                    trailing_comments,
26674                    inferred_type: None,
26675                }))
26676            } else if self.match_token(TokenType::Dash) {
26677                let operator_comments = self.previous_trailing_comments().to_vec();
26678                let right = self.parse_at_time_zone()?;
26679                let trailing_comments = self.previous_trailing_comments().to_vec();
26680                Expression::Sub(Box::new(BinaryOp {
26681                    left,
26682                    right,
26683                    left_comments,
26684                    operator_comments,
26685                    trailing_comments,
26686                    inferred_type: None,
26687                }))
26688            } else if !self.dpipe_is_logical_or() && self.match_token(TokenType::DPipe) {
26689                let operator_comments = self.previous_trailing_comments().to_vec();
26690                let right = self.parse_at_time_zone()?;
26691                let trailing_comments = self.previous_trailing_comments().to_vec();
26692                Expression::Concat(Box::new(BinaryOp {
26693                    left,
26694                    right,
26695                    left_comments,
26696                    operator_comments,
26697                    trailing_comments,
26698                    inferred_type: None,
26699                }))
26700            } else if self.match_token(TokenType::DQMark) {
26701                let right = self.parse_at_time_zone()?;
26702                Expression::Coalesce(Box::new(crate::expressions::VarArgFunc {
26703                    expressions: vec![left, right],
26704                    original_name: None,
26705                    inferred_type: None,
26706                }))
26707            } else {
26708                return Ok(left);
26709            };
26710
26711            left = expr;
26712        }
26713    }
26714
26715    /// Parse AT TIME ZONE expression
26716    fn parse_at_time_zone(&mut self) -> Result<Expression> {
26717        let mut expr = self.parse_multiplication()?;
26718
26719        // Check for AT TIME ZONE (can be chained)
26720        while self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("AT") {
26721            self.skip(); // consume AT
26722                         // Check for TIME ZONE
26723            if self.check(TokenType::Time) {
26724                self.skip(); // consume TIME
26725                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ZONE") {
26726                    self.skip(); // consume ZONE
26727                    let zone = self.parse_unary()?;
26728                    expr = Expression::AtTimeZone(Box::new(AtTimeZone { this: expr, zone }));
26729                } else {
26730                    return Err(self.parse_error("Expected ZONE after AT TIME"));
26731                }
26732            } else {
26733                return Err(self.parse_error("Expected TIME after AT"));
26734            }
26735        }
26736
26737        Ok(expr)
26738    }
26739
26740    /// Parse multiplication/division
26741    fn parse_multiplication(&mut self) -> Result<Expression> {
26742        let mut left = self.parse_power()?;
26743
26744        loop {
26745            let expr = if self.match_token(TokenType::Star) {
26746                let right = self.parse_power()?;
26747                Expression::Mul(Box::new(BinaryOp::new(left, right)))
26748            } else if self.match_token(TokenType::Slash) {
26749                let right = self.parse_power()?;
26750                Expression::Div(Box::new(BinaryOp::new(left, right)))
26751            } else if self.match_token(TokenType::Percent) {
26752                let right = self.parse_power()?;
26753                Expression::Mod(Box::new(BinaryOp::new(left, right)))
26754            } else if !self.check(TokenType::QuotedIdentifier)
26755                && (self.match_identifier("MOD") || self.match_token(TokenType::Mod))
26756            {
26757                // MySQL/Teradata: x MOD y (infix modulo operator)
26758                // Don't match QuotedIdentifier — `MOD` is an identifier alias, not an operator
26759                let right = self.parse_power()?;
26760                Expression::Mod(Box::new(BinaryOp::new(left, right)))
26761            } else if !self.check(TokenType::QuotedIdentifier)
26762                && (self.match_identifier("DIV") || self.match_token(TokenType::Div))
26763            {
26764                // DIV keyword for integer division (Hive/Spark/MySQL/ClickHouse)
26765                // Don't match QuotedIdentifier — `DIV` is an identifier alias, not an operator
26766                // If DIV was matched as a Var (not keyword Div token), verify it's actually
26767                // an operator by checking that a right operand follows. Otherwise it's an alias.
26768                let matched_as_var = self.previous().token_type == TokenType::Var;
26769                if matched_as_var
26770                    && (self.is_at_end()
26771                        || self.check(TokenType::Semicolon)
26772                        || self.check(TokenType::From)
26773                        || self.check(TokenType::Where)
26774                        || self.check(TokenType::Comma)
26775                        || self.check(TokenType::RParen))
26776                {
26777                    // Backtrack: DIV is being used as an alias, not an operator
26778                    self.current -= 1;
26779                    return Ok(left);
26780                }
26781                let right = self.parse_power()?;
26782                Expression::IntDiv(Box::new(crate::expressions::BinaryFunc {
26783                    this: left,
26784                    expression: right,
26785                    original_name: None,
26786                    inferred_type: None,
26787                }))
26788            } else {
26789                return Ok(left);
26790            };
26791
26792            left = expr;
26793        }
26794    }
26795
26796    /// Parse power/exponentiation (**) operator
26797    /// In PostgreSQL/Redshift, ^ (Caret) is POWER, not BitwiseXor
26798    fn parse_power(&mut self) -> Result<Expression> {
26799        let mut left = self.parse_unary()?;
26800
26801        loop {
26802            if self.match_token(TokenType::DStar) {
26803                let right = self.parse_unary()?;
26804                left = Expression::Power(Box::new(BinaryFunc {
26805                    original_name: Some("**".to_string()),
26806                    this: left,
26807                    expression: right,
26808                    inferred_type: None,
26809                }));
26810            } else if matches!(
26811                self.config.dialect,
26812                Some(crate::dialects::DialectType::PostgreSQL)
26813                    | Some(crate::dialects::DialectType::Redshift)
26814                    | Some(crate::dialects::DialectType::DuckDB)
26815            ) && self.match_token(TokenType::Caret)
26816            {
26817                let right = self.parse_unary()?;
26818                left = Expression::Power(Box::new(BinaryFunc {
26819                    original_name: None,
26820                    this: left,
26821                    expression: right,
26822                    inferred_type: None,
26823                }));
26824            } else {
26825                return Ok(left);
26826            }
26827        }
26828    }
26829
26830    /// Try to parse a type literal expression like: point '(4,4)', timestamp '2024-01-01'
26831    /// PostgreSQL allows type name followed by string literal as a cast shorthand.
26832    /// Returns None if not a type literal pattern, so caller can fall through to parse_primary.
26833    fn try_parse_type_literal(&mut self) -> Result<Option<Expression>> {
26834        // Save position for backtracking
26835        let start_pos = self.current;
26836
26837        // Check if we're at an identifier or Var token that could be a type name
26838        if !self.check(TokenType::Identifier) && !self.check(TokenType::Var) {
26839            return Ok(None);
26840        }
26841
26842        // Get the potential type name without consuming
26843        let type_name = self.peek().text.to_ascii_uppercase();
26844
26845        // Check if this looks like a known data type that supports literal syntax
26846        // These are types where PostgreSQL allows TYPE 'value' syntax
26847        // NOTE: DATE, TIME, TIMESTAMP, INTERVAL are NOT here because they have their own
26848        // token types and are handled specially in parse_primary
26849        let is_type_literal_type = matches!(
26850            type_name.as_str(),
26851            // Geometric types (PostgreSQL)
26852            "POINT" | "LINE" | "LSEG" | "BOX" | "PATH" | "POLYGON" | "CIRCLE" |
26853            // Network types (PostgreSQL)
26854            "INET" | "CIDR" | "MACADDR" | "MACADDR8" |
26855            // Other types that support literal syntax
26856            "UUID" | "JSON" | "JSONB" | "XML" | "BIT" | "VARBIT" |
26857            // Range types (PostgreSQL)
26858            "INT4RANGE" | "INT8RANGE" | "NUMRANGE" | "TSRANGE" | "TSTZRANGE" | "DATERANGE"
26859        );
26860
26861        if !is_type_literal_type {
26862            return Ok(None);
26863        }
26864
26865        // Check if the next token (after type name) is a string literal
26866        if self.current + 1 >= self.tokens.len() {
26867            return Ok(None);
26868        }
26869
26870        if self.tokens[self.current + 1].token_type != TokenType::String {
26871            return Ok(None);
26872        }
26873
26874        // This looks like a type literal! Parse it.
26875        // Consume the type name
26876        self.skip();
26877
26878        // Try to parse the data type from the name
26879        let data_type = match self.parse_data_type_from_name(&type_name) {
26880            Ok(dt) => dt,
26881            Err(_) => {
26882                // If we can't parse the type, backtrack
26883                self.current = start_pos;
26884                return Ok(None);
26885            }
26886        };
26887
26888        // Parse the string literal
26889        if !self.check(TokenType::String) {
26890            // Backtrack - something went wrong
26891            self.current = start_pos;
26892            return Ok(None);
26893        }
26894
26895        let string_token = self.advance();
26896        let value = Expression::Literal(Box::new(Literal::String(string_token.text.clone())));
26897
26898        // JSON literal: JSON '"foo"' -> ParseJson expression (matches Python sqlglot)
26899        if matches!(data_type, DataType::Json | DataType::JsonB)
26900            || matches!(type_name.as_str(), "JSON" | "JSONB")
26901        {
26902            return Ok(Some(Expression::ParseJson(Box::new(UnaryFunc {
26903                this: value,
26904                original_name: None,
26905                inferred_type: None,
26906            }))));
26907        }
26908
26909        // Create the Cast expression
26910        Ok(Some(Expression::Cast(Box::new(Cast {
26911            this: value,
26912            to: data_type,
26913            trailing_comments: Vec::new(),
26914            double_colon_syntax: false,
26915            format: None,
26916            default: None,
26917            inferred_type: None,
26918        }))))
26919    }
26920
26921    /// Try to parse type shorthand CAST: INT 1, VARCHAR 'x', STRING 'x', TEXT 'y', etc.
26922    /// In generic mode (no dialect), a type keyword followed by a literal becomes CAST(literal AS type).
26923    /// This matches Python sqlglot's `_parse_types()` behavior.
26924    fn try_parse_type_shorthand_cast(&mut self) -> Result<Option<Expression>> {
26925        // Only apply in generic mode
26926        let is_generic = self.config.dialect.is_none()
26927            || matches!(
26928                self.config.dialect,
26929                Some(crate::dialects::DialectType::Generic)
26930            );
26931        if !is_generic {
26932            return Ok(None);
26933        }
26934
26935        let start_pos = self.current;
26936
26937        // Check if current token is a type keyword
26938        if !self.is_type_keyword() {
26939            return Ok(None);
26940        }
26941
26942        // Don't apply if the type keyword is followed by a left paren (function call)
26943        // or is not followed by a literal
26944        if self.current + 1 >= self.tokens.len() {
26945            return Ok(None);
26946        }
26947
26948        let next_type = self.tokens[self.current + 1].token_type;
26949        // The value after the type keyword must be a literal (number or string)
26950        if !matches!(next_type, TokenType::Number | TokenType::String) {
26951            return Ok(None);
26952        }
26953
26954        // Get the type name
26955        let type_token = self.advance();
26956        let type_name = type_token.text.to_ascii_uppercase();
26957
26958        // Parse the data type
26959        let data_type = match type_name.as_str() {
26960            "INT" | "INTEGER" => DataType::Int {
26961                length: None,
26962                integer_spelling: type_name == "INTEGER",
26963            },
26964            "BIGINT" => DataType::BigInt { length: None },
26965            "SMALLINT" => DataType::SmallInt { length: None },
26966            "TINYINT" => DataType::TinyInt { length: None },
26967            "FLOAT" => DataType::Float {
26968                precision: None,
26969                scale: None,
26970                real_spelling: false,
26971            },
26972            "DOUBLE" => DataType::Double {
26973                precision: None,
26974                scale: None,
26975            },
26976            "DECIMAL" | "NUMERIC" => DataType::Decimal {
26977                precision: None,
26978                scale: None,
26979            },
26980            "REAL" => DataType::Float {
26981                precision: None,
26982                scale: None,
26983                real_spelling: true,
26984            },
26985            "VARCHAR" => DataType::VarChar {
26986                length: None,
26987                parenthesized_length: false,
26988            },
26989            "CHAR" => DataType::Char { length: None },
26990            "TEXT" | "STRING" => DataType::Text,
26991            "BOOLEAN" | "BOOL" => DataType::Boolean,
26992            "BINARY" => DataType::Binary { length: None },
26993            "VARBINARY" => DataType::VarBinary { length: None },
26994            _ => {
26995                // Unknown type, backtrack
26996                self.current = start_pos;
26997                return Ok(None);
26998            }
26999        };
27000
27001        // Parse the literal value
27002        let value = if self.check(TokenType::String) {
27003            let tok = self.advance();
27004            Expression::Literal(Box::new(Literal::String(tok.text.clone())))
27005        } else if self.check(TokenType::Number) {
27006            let tok = self.advance();
27007            Expression::Literal(Box::new(Literal::Number(tok.text.clone())))
27008        } else {
27009            self.current = start_pos;
27010            return Ok(None);
27011        };
27012
27013        // Create the Cast expression
27014        Ok(Some(Expression::Cast(Box::new(Cast {
27015            this: value,
27016            to: data_type,
27017            trailing_comments: Vec::new(),
27018            double_colon_syntax: false,
27019            format: None,
27020            default: None,
27021            inferred_type: None,
27022        }))))
27023    }
27024
27025    /// Parse unary expressions
27026    fn parse_unary(&mut self) -> Result<Expression> {
27027        if self.match_token(TokenType::Plus) {
27028            // Unary plus is a no-op - just parse the inner expression
27029            // This handles +++1 -> 1, +-1 -> -1, etc.
27030            self.parse_unary()
27031        } else if self.match_token(TokenType::Dash) {
27032            let expr = self.parse_unary()?;
27033            Ok(Expression::Neg(Box::new(UnaryOp::new(expr))))
27034        } else if self.match_token(TokenType::Plus) {
27035            // Unary plus: +1, +expr — just return the inner expression (no-op)
27036            self.parse_unary()
27037        } else if self.match_token(TokenType::Tilde) {
27038            let expr = self.parse_unary()?;
27039            Ok(Expression::BitwiseNot(Box::new(UnaryOp::new(expr))))
27040        } else if self.match_token(TokenType::DPipeSlash) {
27041            // ||/ (Cube root - PostgreSQL)
27042            let expr = self.parse_unary()?;
27043            Ok(Expression::Cbrt(Box::new(UnaryFunc::with_name(
27044                expr,
27045                "||/".to_string(),
27046            ))))
27047        } else if self.match_token(TokenType::PipeSlash) {
27048            // |/ (Square root - PostgreSQL)
27049            let expr = self.parse_unary()?;
27050            Ok(Expression::Sqrt(Box::new(UnaryFunc::with_name(
27051                expr,
27052                "|/".to_string(),
27053            ))))
27054        } else if self.check(TokenType::DAt)
27055            && matches!(
27056                self.config.dialect,
27057                Some(crate::dialects::DialectType::DuckDB)
27058            )
27059        {
27060            // DuckDB @ operator: @(-1), @(expr), @-1
27061            // @ is the ABS operator in DuckDB with low precedence
27062            // Python sqlglot: "@": lambda self: exp.Abs(this=self._parse_bitwise())
27063            // This means @col + 1 parses as ABS(col + 1), not ABS(col) + 1
27064            self.skip(); // consume @
27065                         // Parse at bitwise level for correct precedence (matches Python sqlglot)
27066            let expr = self.parse_bitwise_or()?;
27067            Ok(Expression::Abs(Box::new(UnaryFunc::new(expr))))
27068        } else if self.check(TokenType::Var)
27069            && self.peek().text.starts_with('@')
27070            && matches!(
27071                self.config.dialect,
27072                Some(crate::dialects::DialectType::DuckDB)
27073            )
27074        {
27075            // DuckDB @ operator with identifier: @col, @col + 1
27076            // Tokenizer creates "@col" as a single Var token, so we need to handle it here
27077            // Python sqlglot: "@": lambda self: exp.Abs(this=self._parse_bitwise())
27078            let token = self.advance(); // consume @col token
27079            let col_name = &token.text[1..]; // strip leading @
27080
27081            // Create column expression for the identifier part
27082            let col_expr = Expression::boxed_column(Column {
27083                name: Identifier::new(col_name),
27084                table: None,
27085                join_mark: false,
27086                trailing_comments: Vec::new(),
27087                span: None,
27088                inferred_type: None,
27089            });
27090
27091            // Check if followed by operators that should be included in the ABS
27092            // We need to parse any remaining operators at bitwise level
27093            // First, check if there's a binary operator after this column
27094            if self.check(TokenType::Plus)
27095                || self.check(TokenType::Dash)
27096                || self.check(TokenType::Star)
27097                || self.check(TokenType::Slash)
27098                || self.check(TokenType::Percent)
27099                || self.check(TokenType::Amp)
27100                || self.check(TokenType::Pipe)
27101                || self.check(TokenType::Caret)
27102                || self.check(TokenType::LtLt)
27103                || self.check(TokenType::GtGt)
27104            {
27105                // There are more operators - we need to continue parsing at bitwise level
27106                // But parse_bitwise_or expects to start fresh, not continue with existing left
27107                // So we use a helper approach: parse_bitwise_continuation
27108                let full_expr = self.parse_bitwise_continuation(col_expr)?;
27109                Ok(Expression::Abs(Box::new(UnaryFunc::new(full_expr))))
27110            } else {
27111                // Just the column, no more operators
27112                Ok(Expression::Abs(Box::new(UnaryFunc::new(col_expr))))
27113            }
27114        } else if self.check(TokenType::DAt)
27115            && (self.check_next(TokenType::LParen) || self.check_next(TokenType::Dash))
27116        {
27117            // Non-DuckDB dialects: only handle @(expr) and @-expr as ABS
27118            self.skip(); // consume @
27119            let expr = self.parse_bitwise_or()?;
27120            Ok(Expression::Abs(Box::new(UnaryFunc::new(expr))))
27121        } else if self.check(TokenType::Prior)
27122            && !self.check_next(TokenType::As)
27123            && !self.check_next(TokenType::Comma)
27124            && !self.check_next(TokenType::RParen)
27125            && !self.check_next(TokenType::Semicolon)
27126            && self.current + 1 < self.tokens.len()
27127        {
27128            // Oracle PRIOR expression - references parent row's value in hierarchical queries
27129            // Can appear in SELECT list, CONNECT BY, or other expression contexts
27130            // Python sqlglot: "PRIOR": lambda self: self.expression(exp.Prior, this=self._parse_bitwise())
27131            // When followed by AS/comma/rparen/end, treat PRIOR as an identifier (column name)
27132            self.skip(); // consume PRIOR
27133            let expr = self.parse_bitwise_or()?;
27134            Ok(Expression::Prior(Box::new(Prior { this: expr })))
27135        } else {
27136            // Try to parse type literals like: point '(4,4)', timestamp '2024-01-01', interval '1 day'
27137            // PostgreSQL allows type name followed by string literal as a cast shorthand
27138            if let Some(type_literal) = self.try_parse_type_literal()? {
27139                return self.parse_postfix_operators(type_literal);
27140            }
27141            // Try to parse type shorthand CAST: INT 1, VARCHAR 'x', STRING 'x', TEXT 'y', etc.
27142            // In generic mode, type keyword followed by literal -> CAST(literal AS type)
27143            if let Some(type_cast) = self.try_parse_type_shorthand_cast()? {
27144                return self.parse_postfix_operators(type_cast);
27145            }
27146            let expr = self.parse_primary()?;
27147            // Handle postfix exclamation mark for Snowflake model attribute syntax: model!PREDICT(...)
27148            self.parse_postfix_operators(expr)
27149        }
27150    }
27151
27152    /// Parse postfix operators like ! (model attribute in Snowflake) and : (JSON path in Snowflake)
27153    fn parse_postfix_operators(&mut self, mut expr: Expression) -> Result<Expression> {
27154        // Handle Oracle/Redshift outer join marker (+) after column reference
27155        // Syntax: column_ref (+) indicates optional side of join
27156        if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
27157            // Look ahead to verify it's ( + )
27158            let saved_pos = self.current;
27159            if self.match_token(TokenType::LParen)
27160                && self.match_token(TokenType::Plus)
27161                && self.match_token(TokenType::RParen)
27162            {
27163                // Set join_mark on the column expression
27164                if let Expression::Column(ref mut col) = expr {
27165                    col.join_mark = true;
27166                }
27167            } else {
27168                self.current = saved_pos;
27169            }
27170        }
27171
27172        // Handle EXCLAMATION for Snowflake model attribute syntax: model!PREDICT(...)
27173        while self.match_token(TokenType::Exclamation) {
27174            // Parse the attribute/function after the exclamation mark
27175            // This can be either a simple identifier (model!admin) or a function call (model!PREDICT(1))
27176            let attr = self.parse_primary()?;
27177            expr = Expression::ModelAttribute(Box::new(ModelAttribute {
27178                this: Box::new(expr),
27179                expression: Box::new(attr),
27180            }));
27181        }
27182
27183        // Handle COLON for Snowflake JSON path extraction: a:field or a:field.subfield
27184        // This creates JSONExtract expressions that transform to GET_PATH(a, 'field') in Snowflake
27185        expr = self.parse_colon_json_path(expr)?;
27186
27187        // Handle DCOLON (::) - in SingleStore it's JSON extraction, in other dialects it's cast
27188        // SingleStore JSON path syntax:
27189        //   a::b -> JSON_EXTRACT_JSON(a, 'b')
27190        //   a::$b -> JSON_EXTRACT_STRING(a, 'b')
27191        //   a::%b -> JSON_EXTRACT_DOUBLE(a, 'b')
27192        //   a::?names -> JSON match syntax
27193        if matches!(
27194            self.config.dialect,
27195            Some(crate::dialects::DialectType::SingleStore)
27196        ) {
27197            expr = self.parse_singlestore_json_path(expr)?;
27198        } else {
27199            // For other dialects, :: is cast syntax
27200            // IMPORTANT: Use parse_data_type_for_cast to avoid consuming subscripts as array dimensions
27201            // e.g., ::VARIANT[0] should be cast to VARIANT followed by subscript [0]
27202            while self.match_token(TokenType::DColon) {
27203                let data_type = self.parse_data_type_for_cast()?;
27204                expr = Expression::Cast(Box::new(Cast {
27205                    this: expr,
27206                    to: data_type,
27207                    trailing_comments: Vec::new(),
27208                    double_colon_syntax: true,
27209                    format: None,
27210                    default: None,
27211                    inferred_type: None,
27212                }));
27213            }
27214        }
27215
27216        // Teradata: (FORMAT '...') phrase after an expression
27217        if matches!(
27218            self.config.dialect,
27219            Some(crate::dialects::DialectType::Teradata)
27220        ) && self.check(TokenType::LParen)
27221            && self.check_next(TokenType::Format)
27222        {
27223            self.skip(); // consume (
27224            self.skip(); // consume FORMAT
27225            let format = self.expect_string()?;
27226            self.expect(TokenType::RParen)?;
27227            expr = Expression::FormatPhrase(Box::new(FormatPhrase {
27228                this: Box::new(expr),
27229                format,
27230            }));
27231        }
27232
27233        Ok(expr)
27234    }
27235
27236    /// Parse SingleStore JSON path extraction syntax
27237    /// Examples:
27238    ///   a::b -> JSON_EXTRACT_JSON(a, 'b')
27239    ///   a::$b -> JSON_EXTRACT_STRING(a, 'b')
27240    ///   a::%b -> JSON_EXTRACT_DOUBLE(a, 'b')
27241    ///   a::`b`::`2` -> nested JSON extraction
27242    fn parse_singlestore_json_path(&mut self, mut expr: Expression) -> Result<Expression> {
27243        loop {
27244            if self.match_token(TokenType::DColon) {
27245                // :: followed by identifier -> JSON_EXTRACT_JSON
27246                // Check if next is a backtick-quoted identifier or regular identifier
27247                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27248                    self.advance().text
27249                } else if self.check(TokenType::Number) {
27250                    // a::2 -> JSON_EXTRACT_JSON(a, '2')
27251                    self.advance().text
27252                } else {
27253                    return Err(self.parse_error("Expected identifier after ::"));
27254                };
27255
27256                expr = Expression::Function(Box::new(Function::new(
27257                    "JSON_EXTRACT_JSON".to_string(),
27258                    vec![expr, Expression::string(&path_key)],
27259                )));
27260            } else if self.match_token(TokenType::DColonDollar) {
27261                // ::$ followed by identifier -> JSON_EXTRACT_STRING
27262                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27263                    self.advance().text
27264                } else {
27265                    return Err(self.parse_error("Expected identifier after ::$"));
27266                };
27267
27268                expr = Expression::Function(Box::new(Function::new(
27269                    "JSON_EXTRACT_STRING".to_string(),
27270                    vec![expr, Expression::string(&path_key)],
27271                )));
27272            } else if self.match_token(TokenType::DColonPercent) {
27273                // ::% followed by identifier -> JSON_EXTRACT_DOUBLE
27274                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27275                    self.advance().text
27276                } else {
27277                    return Err(self.parse_error("Expected identifier after ::%"));
27278                };
27279
27280                expr = Expression::Function(Box::new(Function::new(
27281                    "JSON_EXTRACT_DOUBLE".to_string(),
27282                    vec![expr, Expression::string(&path_key)],
27283                )));
27284            } else if self.match_token(TokenType::DColonQMark) {
27285                // ::? followed by identifier -> Keep as JSONMatchAny expression for now
27286                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27287                    self.advance().text
27288                } else {
27289                    return Err(self.parse_error("Expected identifier after ::?"));
27290                };
27291
27292                // For now, create a function that will be handled specially
27293                expr = Expression::Function(Box::new(Function::new(
27294                    "JSON_EXTRACT_JSON".to_string(), // placeholder
27295                    vec![expr, Expression::string(&format!("?{}", path_key))],
27296                )));
27297            } else {
27298                break;
27299            }
27300        }
27301        Ok(expr)
27302    }
27303
27304    /// Parse colon-separated JSON path syntax (Snowflake variant extraction)
27305    /// Examples:
27306    ///   a:from -> GET_PATH(a, 'from')
27307    ///   a:b.c.d -> GET_PATH(a, 'b.c.d')
27308    ///   a:from::STRING -> CAST(GET_PATH(a, 'from') AS VARCHAR)
27309    ///   a:b:c.d -> GET_PATH(a, 'b.c.d') (multiple colons joined into single path)
27310    fn parse_colon_json_path(&mut self, mut this: Expression) -> Result<Expression> {
27311        // DuckDB uses colon for prefix alias syntax (e.g., "alias: expr" means "expr AS alias")
27312        // Skip JSON path extraction for DuckDB - it's handled separately in parse_select_expressions
27313        if matches!(
27314            self.config.dialect,
27315            Some(crate::dialects::DialectType::DuckDB)
27316        ) {
27317            return Ok(this);
27318        }
27319
27320        // ClickHouse uses : as part of the ternary operator (condition ? true : false)
27321        // Skip JSON path extraction for ClickHouse to avoid consuming the ternary separator
27322        if matches!(
27323            self.config.dialect,
27324            Some(crate::dialects::DialectType::ClickHouse)
27325        ) {
27326            return Ok(this);
27327        }
27328
27329        // Only apply colon JSON path parsing to identifiers, columns, and function results
27330        // This prevents {'key': 'value'} object literals from being misinterpreted
27331        let is_valid_json_path_base = matches!(
27332            &this,
27333            Expression::Column(_) |
27334            Expression::Identifier(_) |
27335            Expression::Dot(_) |
27336            Expression::JSONExtract(_) |  // Allow chained paths like a:b:c
27337            Expression::Function(_) |     // Allow function results like PARSE_JSON(...):x
27338            Expression::ParseJson(_) |    // Allow PARSE_JSON specifically
27339            Expression::Parameter(_) // Allow positional params like $1:name
27340        );
27341
27342        if !is_valid_json_path_base {
27343            return Ok(this);
27344        }
27345
27346        // Check if we have a colon (but NOT double-colon which is cast syntax)
27347        if !self.check(TokenType::Colon) {
27348            return Ok(this);
27349        }
27350
27351        // Make sure this is not a double-colon (::) which is cast syntax
27352        if self.check_next(TokenType::Colon) {
27353            // This is :: (DColon should have been tokenized, but just in case)
27354            return Ok(this);
27355        }
27356
27357        // Collect ALL the JSON path parts across multiple colons
27358        // a:b.c:d.e -> GET_PATH(a, 'b.c.d.e')
27359        // a:b[0].c -> GET_PATH(a, 'b[0].c')
27360        let mut path_string = String::new();
27361
27362        // Parse all colon-separated path segments
27363        while self.check(TokenType::Colon) && !self.check_next(TokenType::Colon) {
27364            // Save position before consuming colon so we can backtrack
27365            // if what follows isn't a valid JSON path component (e.g., DuckDB's "foo: 1" label syntax)
27366            let saved_pos = self.current;
27367            let saved_path_len = path_string.len();
27368
27369            // Consume the colon
27370            self.skip();
27371
27372            // Parse first path component (required) - can be any identifier including keywords
27373            // Also handle backtick-quoted identifiers like `zip code` or `fb:testid`
27374            // Also handle bracket notation directly after colon: c1:['price'] or c1:["foo bar"]
27375            // IMPORTANT: Check QuotedIdentifier FIRST since is_identifier_token() includes QuotedIdentifier
27376            let mut had_initial_component = false;
27377            if self.check(TokenType::QuotedIdentifier) {
27378                // Quoted field name in variant access
27379                // Snowflake: v:"fruit" → double-quoted key → stored as plain text 'fruit'
27380                // Databricks: raw:`zip code` → backtick-quoted key → stored as bracket notation '["zip code"]'
27381                let quoted_name = self.advance().text.clone();
27382                let is_snowflake = matches!(
27383                    self.config.dialect,
27384                    Some(crate::dialects::DialectType::Snowflake)
27385                );
27386                let needs_bracket = quoted_name.contains(' ') || quoted_name.contains('\'');
27387                if is_snowflake && !needs_bracket {
27388                    // Snowflake double-quoted keys without special chars are stored as plain text
27389                    // Add dot separator for plain segments
27390                    if !path_string.is_empty() {
27391                        path_string.push('.');
27392                    }
27393                    path_string.push_str(&quoted_name);
27394                } else if is_snowflake && needs_bracket {
27395                    // Snowflake keys with spaces/apostrophes use bracket notation: ["key with spaces"]
27396                    // No dot before bracket notation
27397                    path_string.push_str("[\"");
27398                    // Don't escape single quotes here - the generator will handle escaping
27399                    // when outputting the string literal
27400                    path_string.push_str(&quoted_name);
27401                    path_string.push_str("\"]");
27402                } else {
27403                    // Other dialects (Databricks): wrap in bracket notation
27404                    // No dot before bracket notation
27405                    path_string.push_str("[\"");
27406                    for c in quoted_name.chars() {
27407                        if c == '"' {
27408                            path_string.push_str("\\\"");
27409                        } else {
27410                            path_string.push(c);
27411                        }
27412                    }
27413                    path_string.push_str("\"]");
27414                }
27415                had_initial_component = true;
27416            } else if self.is_identifier_token()
27417                || self.is_safe_keyword_as_identifier()
27418                || self.is_reserved_keyword_as_identifier()
27419            {
27420                // Add a dot separator for plain identifier segments
27421                if !path_string.is_empty() {
27422                    path_string.push('.');
27423                }
27424                let first_part = self.advance().text;
27425                path_string.push_str(&first_part);
27426                had_initial_component = true;
27427            } else if self.check(TokenType::LBracket) {
27428                // Bracket notation directly after colon: c1:['price'] or c1:["foo bar"]
27429                // Mark that we have a valid path start - the bracket will be parsed in the loop below
27430                had_initial_component = true;
27431            }
27432
27433            if !had_initial_component {
27434                // Not a valid JSON path component - backtrack and stop
27435                // This handles cases like DuckDB's "foo: 1" label/alias syntax
27436                // where the colon is followed by a non-identifier (e.g., a number)
27437                self.current = saved_pos;
27438                path_string.truncate(saved_path_len);
27439                break;
27440            }
27441
27442            // Parse optional array indices and additional path components
27443            loop {
27444                // Handle array index: [0], [1], [*], ['key'], ["key"], etc.
27445                if self.match_token(TokenType::LBracket) {
27446                    // Parse the index expression (typically a number, identifier, * for wildcard, or string key)
27447                    if self.check(TokenType::Number) {
27448                        path_string.push('[');
27449                        let idx = self.advance().text;
27450                        path_string.push_str(&idx);
27451                        self.expect(TokenType::RBracket)?;
27452                        path_string.push(']');
27453                    } else if self.check(TokenType::Star) {
27454                        // Wildcard array access: [*] matches all array elements
27455                        path_string.push('[');
27456                        self.skip();
27457                        path_string.push('*');
27458                        self.expect(TokenType::RBracket)?;
27459                        path_string.push(']');
27460                    } else if self.check(TokenType::String) {
27461                        // Single-quoted string key access: ['bicycle']
27462                        // Convert to dot notation for simple keys, keep bracket notation for keys with spaces
27463                        let key = self.advance().text;
27464                        self.expect(TokenType::RBracket)?;
27465                        // Check if the key contains spaces or special characters that require bracket notation
27466                        let needs_brackets =
27467                            key.contains(' ') || key.contains('"') || key.contains('\'');
27468                        if needs_brackets {
27469                            // Keep bracket notation with double quotes: ["zip code"]
27470                            path_string.push_str("[\"");
27471                            for c in key.chars() {
27472                                if c == '"' {
27473                                    path_string.push_str("\\\"");
27474                                } else {
27475                                    path_string.push(c);
27476                                }
27477                            }
27478                            path_string.push_str("\"]");
27479                        } else {
27480                            // Convert to dot notation: store['bicycle'] -> store.bicycle
27481                            // But only add dot if path_string is not empty (handles c1:['price'] -> c1:price)
27482                            if !path_string.is_empty() {
27483                                path_string.push('.');
27484                            }
27485                            path_string.push_str(&key);
27486                        }
27487                    } else if self.check(TokenType::QuotedIdentifier) {
27488                        // Double-quoted string key access: ["zip code"]
27489                        // These are tokenized as QuotedIdentifier, not String
27490                        // Must be checked BEFORE is_identifier_token() since it includes QuotedIdentifier
27491                        let key = self.advance().text;
27492                        self.expect(TokenType::RBracket)?;
27493                        // Always use bracket notation with double quotes for quoted identifiers
27494                        path_string.push_str("[\"");
27495                        for c in key.chars() {
27496                            if c == '"' {
27497                                path_string.push_str("\\\"");
27498                            } else {
27499                                path_string.push(c);
27500                            }
27501                        }
27502                        path_string.push_str("\"]");
27503                    } else if self.is_identifier_token() {
27504                        // Check if this is a "dynamic bracket" — a column reference like s.x
27505                        // inside brackets. We detect this by checking if the identifier is
27506                        // followed by a dot (making it a qualified column reference).
27507                        let saved_bracket_pos = self.current;
27508                        let ident_text = self.advance().text.clone();
27509                        if self.check(TokenType::Dot) {
27510                            // Dynamic bracket: [s.x] where s.x is a column reference
27511                            // Backtrack to before the identifier so we can parse the full expression
27512                            self.current = saved_bracket_pos;
27513                            // Parse the full expression inside the brackets
27514                            let index_expr = self.parse_expression()?;
27515                            self.expect(TokenType::RBracket)?;
27516
27517                            // Build JSONExtract for the path accumulated so far
27518                            let path_expr =
27519                                Expression::Literal(Box::new(Literal::String(path_string)));
27520                            let json_extract = Expression::JSONExtract(Box::new(JSONExtract {
27521                                this: Box::new(this),
27522                                expression: Box::new(path_expr),
27523                                only_json_types: None,
27524                                expressions: Vec::new(),
27525                                variant_extract: Some(Box::new(Expression::Boolean(
27526                                    BooleanLiteral { value: true },
27527                                ))),
27528                                json_query: None,
27529                                option: None,
27530                                quote: None,
27531                                on_condition: None,
27532                                requires_json: None,
27533                            }));
27534
27535                            // Wrap in Subscript
27536                            let subscript = Expression::Subscript(Box::new(Subscript {
27537                                this: json_extract,
27538                                index: index_expr,
27539                            }));
27540
27541                            // Now continue parsing any remaining path after the dynamic bracket.
27542                            // This handles patterns like [s.x].r.d or [s.x]:r or [s.x].r.d[s.y]
27543                            // We parse dots into a new path string, and if we encounter another
27544                            // dynamic bracket, we recurse.
27545                            let mut suffix_path = String::new();
27546                            loop {
27547                                if self.match_token(TokenType::Dot) {
27548                                    // Dot access after dynamic bracket: [s.x].r.d
27549                                    if !suffix_path.is_empty() {
27550                                        suffix_path.push('.');
27551                                    }
27552                                    if self.is_identifier_token()
27553                                        || self.is_safe_keyword_as_identifier()
27554                                        || self.is_reserved_keyword_as_identifier()
27555                                    {
27556                                        let part = self.advance().text;
27557                                        suffix_path.push_str(&part);
27558                                    } else {
27559                                        return Err(self.parse_error(
27560                                            "Expected identifier after . in JSON path",
27561                                        ));
27562                                    }
27563                                } else if self.check(TokenType::LBracket) {
27564                                    // Another bracket after dot path: [s.x].r.d[s.y]
27565                                    // We need to check if this bracket contains a dynamic expression
27566                                    break;
27567                                } else {
27568                                    break;
27569                                }
27570                            }
27571
27572                            // Build the result depending on whether there are suffix dot paths
27573                            let result_base = if suffix_path.is_empty() {
27574                                subscript
27575                            } else {
27576                                // Create another JSONExtract for the suffix path
27577                                Expression::JSONExtract(Box::new(JSONExtract {
27578                                    this: Box::new(subscript),
27579                                    expression: Box::new(Expression::Literal(Box::new(
27580                                        Literal::String(suffix_path),
27581                                    ))),
27582                                    only_json_types: None,
27583                                    expressions: Vec::new(),
27584                                    variant_extract: Some(Box::new(Expression::Boolean(
27585                                        BooleanLiteral { value: true },
27586                                    ))),
27587                                    json_query: None,
27588                                    option: None,
27589                                    quote: None,
27590                                    on_condition: None,
27591                                    requires_json: None,
27592                                }))
27593                            };
27594
27595                            // Check for another bracket (e.g., [s.y] after .r.d)
27596                            if self.match_token(TokenType::LBracket) {
27597                                // Parse the index expression
27598                                let index_expr2 = self.parse_expression()?;
27599                                self.expect(TokenType::RBracket)?;
27600                                let subscript2 = Expression::Subscript(Box::new(Subscript {
27601                                    this: result_base,
27602                                    index: index_expr2,
27603                                }));
27604                                // Update `this` and `path_string` so we properly continue the outer loop
27605                                this = subscript2;
27606                                path_string = String::new();
27607                            } else {
27608                                this = result_base;
27609                                path_string = String::new();
27610                            }
27611
27612                            // Continue parsing more colon segments or break
27613                            // Need to break out of the inner loop to let the outer while loop
27614                            // check for more colon segments
27615                            break;
27616                        } else {
27617                            // Simple identifier index: [idx]
27618                            path_string.push('[');
27619                            path_string.push_str(&ident_text);
27620                            self.expect(TokenType::RBracket)?;
27621                            path_string.push(']');
27622                        }
27623                    } else {
27624                        // Empty brackets or unexpected token - just close the bracket
27625                        path_string.push('[');
27626                        self.expect(TokenType::RBracket)?;
27627                        path_string.push(']');
27628                    }
27629                } else if self.match_token(TokenType::Dot) {
27630                    // Handle dot access
27631                    path_string.push('.');
27632                    if self.is_identifier_token()
27633                        || self.is_safe_keyword_as_identifier()
27634                        || self.is_reserved_keyword_as_identifier()
27635                    {
27636                        let part = self.advance().text;
27637                        path_string.push_str(&part);
27638                    } else {
27639                        return Err(self.parse_error("Expected identifier after . in JSON path"));
27640                    }
27641                } else {
27642                    break;
27643                }
27644            }
27645        }
27646
27647        // If no path was parsed (e.g., backtracked on first colon), return the original expression
27648        if path_string.is_empty() {
27649            return Ok(this);
27650        }
27651
27652        // Create the JSONExtract expression with variant_extract marker
27653        let path_expr = Expression::Literal(Box::new(Literal::String(path_string)));
27654        let json_extract = Expression::JSONExtract(Box::new(JSONExtract {
27655            this: Box::new(this),
27656            expression: Box::new(path_expr),
27657            only_json_types: None,
27658            expressions: Vec::new(),
27659            variant_extract: Some(Box::new(Expression::Boolean(BooleanLiteral {
27660                value: true,
27661            }))),
27662            json_query: None,
27663            option: None,
27664            quote: None,
27665            on_condition: None,
27666            requires_json: None,
27667        }));
27668
27669        Ok(json_extract)
27670    }
27671
27672    /// Check if the current token is a reserved keyword that can be used as identifier in JSON path
27673    fn is_reserved_keyword_as_identifier(&self) -> bool {
27674        if self.is_at_end() {
27675            return false;
27676        }
27677        let token = self.peek();
27678        // Allow reserved keywords like FROM, SELECT, etc. as JSON path components
27679        matches!(
27680            token.token_type,
27681            TokenType::From
27682                | TokenType::Select
27683                | TokenType::Where
27684                | TokenType::And
27685                | TokenType::Or
27686                | TokenType::Not
27687                | TokenType::In
27688                | TokenType::As
27689                | TokenType::On
27690                | TokenType::Join
27691                | TokenType::Left
27692                | TokenType::Right
27693                | TokenType::Inner
27694                | TokenType::Outer
27695                | TokenType::Cross
27696                | TokenType::Full
27697                | TokenType::Group
27698                | TokenType::Order
27699                | TokenType::By
27700                | TokenType::Having
27701                | TokenType::Limit
27702                | TokenType::Offset
27703                | TokenType::Union
27704                | TokenType::Except
27705                | TokenType::Intersect
27706                | TokenType::All
27707                | TokenType::Distinct
27708                | TokenType::Case
27709                | TokenType::When
27710                | TokenType::Then
27711                | TokenType::Else
27712                | TokenType::End
27713                | TokenType::Null
27714                | TokenType::True
27715                | TokenType::False
27716                | TokenType::Between
27717                | TokenType::Like
27718                | TokenType::Is
27719                | TokenType::Exists
27720                | TokenType::Insert
27721                | TokenType::Update
27722                | TokenType::Delete
27723                | TokenType::Create
27724                | TokenType::Alter
27725                | TokenType::Drop
27726                | TokenType::Table
27727                | TokenType::View
27728                | TokenType::Index
27729                | TokenType::Set
27730                | TokenType::Values
27731                | TokenType::Into
27732                | TokenType::Default
27733                | TokenType::Key
27734                | TokenType::Unique
27735                | TokenType::Check
27736                | TokenType::Constraint
27737                | TokenType::References
27738        )
27739    }
27740
27741    /// Parse primary expressions
27742    fn parse_primary(&mut self) -> Result<Expression> {
27743        // Handle APPROXIMATE COUNT(DISTINCT expr) - Redshift syntax
27744        // Parses as ApproxDistinct expression
27745        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("APPROXIMATE") {
27746            let saved_pos = self.current;
27747            self.skip(); // consume APPROXIMATE
27748                         // Parse the COUNT(DISTINCT ...) that follows
27749            let func = self.parse_primary()?;
27750            // Check if it's COUNT with DISTINCT
27751            if let Expression::Count(ref count_expr) = func {
27752                if count_expr.distinct {
27753                    let this_expr = count_expr.this.clone().unwrap_or_else(|| {
27754                        Expression::Star(crate::expressions::Star {
27755                            table: None,
27756                            except: None,
27757                            replace: None,
27758                            rename: None,
27759                            trailing_comments: Vec::new(),
27760                            span: None,
27761                        })
27762                    });
27763                    return Ok(Expression::ApproxDistinct(Box::new(
27764                        crate::expressions::AggFunc {
27765                            this: this_expr,
27766                            distinct: false,
27767                            filter: None,
27768                            order_by: Vec::new(),
27769                            name: Some("APPROX_DISTINCT".to_string()),
27770                            ignore_nulls: None,
27771                            having_max: None,
27772                            limit: None,
27773                            inferred_type: None,
27774                        },
27775                    )));
27776                }
27777            }
27778            // Not COUNT(DISTINCT ...) - backtrack
27779            self.current = saved_pos;
27780        }
27781
27782        if let Some(connect_by_root) = self.try_parse_connect_by_root_expression()? {
27783            return Ok(connect_by_root);
27784        }
27785
27786        // PostgreSQL VARIADIC prefix in function call arguments
27787        // e.g., SELECT MLEAST(VARIADIC ARRAY[10, -1, 5, 4.4])
27788        if matches!(
27789            self.config.dialect,
27790            Some(crate::dialects::DialectType::PostgreSQL)
27791                | Some(crate::dialects::DialectType::Redshift)
27792        ) {
27793            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("VARIADIC") {
27794                self.skip(); // consume VARIADIC
27795                let expr = self.parse_bitwise_or()?;
27796                return Ok(Expression::Variadic(Box::new(
27797                    crate::expressions::Variadic {
27798                        this: Box::new(expr),
27799                    },
27800                )));
27801            }
27802        }
27803
27804        // MySQL charset introducer: _utf8mb4 'string', _latin1 x'hex', etc.
27805        if matches!(
27806            self.config.dialect,
27807            Some(crate::dialects::DialectType::MySQL)
27808                | Some(crate::dialects::DialectType::SingleStore)
27809                | Some(crate::dialects::DialectType::Doris)
27810                | Some(crate::dialects::DialectType::StarRocks)
27811        ) {
27812            if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
27813                if self.peek().text.starts_with('_')
27814                    && Self::is_mysql_charset_introducer(&self.peek().text.to_ascii_uppercase())
27815                {
27816                    // Check if next token is a string literal or hex string
27817                    if self.current + 1 < self.tokens.len() {
27818                        let next_tt = self.tokens[self.current + 1].token_type;
27819                        if matches!(
27820                            next_tt,
27821                            TokenType::String | TokenType::HexString | TokenType::BitString
27822                        ) {
27823                            let charset_token = self.advance(); // consume charset name
27824                            let charset_name = charset_token.text.clone();
27825                            let literal = self.parse_primary()?; // parse the string/hex literal
27826                            return Ok(Expression::Introducer(Box::new(
27827                                crate::expressions::Introducer {
27828                                    this: Box::new(Expression::Column(Box::new(
27829                                        crate::expressions::Column {
27830                                            name: crate::expressions::Identifier {
27831                                                name: charset_name,
27832                                                quoted: false,
27833                                                trailing_comments: Vec::new(),
27834                                                span: None,
27835                                            },
27836                                            table: None,
27837                                            join_mark: false,
27838                                            trailing_comments: Vec::new(),
27839                                            span: None,
27840                                            inferred_type: None,
27841                                        },
27842                                    ))),
27843                                    expression: Box::new(literal),
27844                                },
27845                            )));
27846                        }
27847                    }
27848                }
27849            }
27850        }
27851
27852        // Array literal: [1, 2, 3] or comprehension: [expr FOR var IN iterator]
27853        if self.match_token(TokenType::LBracket) {
27854            // Parse empty array: []
27855            if self.match_token(TokenType::RBracket) {
27856                return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
27857                    expressions: Vec::new(),
27858                    bracket_notation: true,
27859                    use_list_keyword: false,
27860                })));
27861            }
27862
27863            // Parse first expression
27864            let first_expr = self.parse_expression()?;
27865
27866            // Check for comprehension syntax: [expr FOR var IN iterator [IF condition]]
27867            if self.match_token(TokenType::For) {
27868                // Parse loop variable - typically a simple identifier like 'x'
27869                let loop_var = self.parse_primary()?;
27870
27871                // Parse optional position (second variable after comma)
27872                let position = if self.match_token(TokenType::Comma) {
27873                    Some(self.parse_primary()?)
27874                } else {
27875                    None
27876                };
27877
27878                // Expect IN keyword
27879                if !self.match_token(TokenType::In) {
27880                    return Err(self.parse_error("Expected IN in comprehension"));
27881                }
27882
27883                // Parse iterator expression
27884                let iterator = self.parse_expression()?;
27885
27886                // Parse optional condition after IF
27887                let condition = if self.match_token(TokenType::If) {
27888                    Some(self.parse_expression()?)
27889                } else {
27890                    None
27891                };
27892
27893                // Expect closing bracket
27894                self.expect(TokenType::RBracket)?;
27895
27896                // Return Comprehension
27897                return Ok(Expression::Comprehension(Box::new(Comprehension {
27898                    this: Box::new(first_expr),
27899                    expression: Box::new(loop_var),
27900                    position: position.map(Box::new),
27901                    iterator: Some(Box::new(iterator)),
27902                    condition: condition.map(Box::new),
27903                })));
27904            }
27905
27906            // Regular array - continue parsing elements
27907            // ClickHouse allows AS aliases in array: [1 AS a, 2 AS b]
27908            let first_expr = if matches!(
27909                self.config.dialect,
27910                Some(crate::dialects::DialectType::ClickHouse)
27911            ) && self.check(TokenType::As)
27912                && !self.check_next(TokenType::RBracket)
27913            {
27914                self.skip(); // consume AS
27915                let alias = self.expect_identifier()?;
27916                Expression::Alias(Box::new(Alias::new(first_expr, Identifier::new(alias))))
27917            } else {
27918                first_expr
27919            };
27920            let mut expressions = vec![first_expr];
27921            while self.match_token(TokenType::Comma) {
27922                // Handle trailing comma
27923                if self.check(TokenType::RBracket) {
27924                    break;
27925                }
27926                let expr = self.parse_expression()?;
27927                // ClickHouse: handle AS alias on array elements
27928                let expr = if matches!(
27929                    self.config.dialect,
27930                    Some(crate::dialects::DialectType::ClickHouse)
27931                ) && self.check(TokenType::As)
27932                    && !self.check_next(TokenType::RBracket)
27933                {
27934                    self.skip(); // consume AS
27935                    let alias = self.expect_identifier()?;
27936                    Expression::Alias(Box::new(Alias::new(expr, Identifier::new(alias))))
27937                } else {
27938                    expr
27939                };
27940                expressions.push(expr);
27941            }
27942            self.expect(TokenType::RBracket)?;
27943            return self.maybe_parse_subscript(Expression::ArrayFunc(Box::new(ArrayConstructor {
27944                expressions,
27945                bracket_notation: true,
27946                use_list_keyword: false,
27947            })));
27948        }
27949
27950        // Map/Struct literal with curly braces: {'a': 1, 'b': 2}
27951        // Or Snowflake wildcard syntax: {*}, {tbl.*}, {* EXCLUDE (...)}, {* ILIKE '...'}
27952        if self.match_token(TokenType::LBrace) {
27953            // ClickHouse query parameter: {name: Type}
27954            // We consumed `{` above, so rewind and let the dedicated parser consume it.
27955            if matches!(
27956                self.config.dialect,
27957                Some(crate::dialects::DialectType::ClickHouse)
27958            ) {
27959                self.current -= 1;
27960                if let Some(param) = self.parse_clickhouse_braced_parameter()? {
27961                    return self.maybe_parse_subscript(param);
27962                }
27963                // Not a ClickHouse query parameter, restore position after `{` for map/wildcard parsing.
27964                self.current += 1;
27965            }
27966
27967            // Parse empty map: {}
27968            if self.match_token(TokenType::RBrace) {
27969                return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
27970                    keys: Vec::new(),
27971                    values: Vec::new(),
27972                    curly_brace_syntax: true,
27973                    with_map_keyword: false,
27974                })));
27975            }
27976
27977            // Check for ODBC escape syntax: {fn function_name(args)}
27978            // This must be checked before wildcards and map literals
27979            if self.check_identifier("fn") {
27980                self.skip(); // consume 'fn'
27981                             // Parse function call
27982                let func_name = self.expect_identifier_or_keyword_with_quoted()?;
27983                self.expect(TokenType::LParen)?;
27984
27985                // Parse function arguments
27986                let mut args = Vec::new();
27987                if !self.check(TokenType::RParen) {
27988                    loop {
27989                        args.push(self.parse_expression()?);
27990                        if !self.match_token(TokenType::Comma) {
27991                            break;
27992                        }
27993                    }
27994                }
27995                self.expect(TokenType::RParen)?;
27996                self.expect(TokenType::RBrace)?;
27997
27998                // Return as a regular function call (the ODBC escape is just syntax sugar)
27999                return Ok(Expression::Function(Box::new(Function::new(
28000                    func_name.name,
28001                    args,
28002                ))));
28003            }
28004
28005            // Check for ODBC datetime literals: {d'2024-01-01'}, {t'12:00:00'}, {ts'2024-01-01 12:00:00'}
28006            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
28007                let type_text = self.peek().text.to_lowercase();
28008                if (type_text == "d" || type_text == "t" || type_text == "ts")
28009                    && self.check_next(TokenType::String)
28010                {
28011                    self.skip(); // consume type indicator (d, t, or ts)
28012                    let value = self.expect_string()?;
28013                    self.expect(TokenType::RBrace)?;
28014
28015                    // Return appropriate expression based on type
28016                    return match type_text.as_str() {
28017                        "d" => Ok(Expression::Date(Box::new(
28018                            crate::expressions::UnaryFunc::new(Expression::Literal(Box::new(
28019                                crate::expressions::Literal::String(value),
28020                            ))),
28021                        ))),
28022                        "t" => Ok(Expression::Time(Box::new(
28023                            crate::expressions::UnaryFunc::new(Expression::Literal(Box::new(
28024                                crate::expressions::Literal::String(value),
28025                            ))),
28026                        ))),
28027                        "ts" => Ok(Expression::Timestamp(Box::new(
28028                            crate::expressions::TimestampFunc {
28029                                this: Some(Box::new(Expression::Literal(Box::new(
28030                                    crate::expressions::Literal::String(value),
28031                                )))),
28032                                zone: None,
28033                                with_tz: None,
28034                                safe: None,
28035                            },
28036                        ))),
28037                        _ => {
28038                            Err(self
28039                                .parse_error(format!("Unknown ODBC datetime type: {}", type_text)))
28040                        }
28041                    };
28042                }
28043            }
28044
28045            // Check for Snowflake wildcard syntax: {*}, {tbl.*}, {* EXCLUDE (...)}, {* ILIKE '...'}
28046            // Pattern: either {*...} or {identifier/var followed by .*}
28047            // Note: Identifiers may be tokenized as Var or Identifier
28048            let is_table_star = (self.check(TokenType::Identifier) || self.check(TokenType::Var))
28049                && self.check_next(TokenType::Dot)
28050                && self
28051                    .tokens
28052                    .get(self.current + 2)
28053                    .map(|t| t.token_type == TokenType::Star)
28054                    .unwrap_or(false);
28055            let is_wildcard = self.check(TokenType::Star) || is_table_star;
28056
28057            if is_wildcard {
28058                // Parse the wildcard expression
28059                let wildcard_expr = if self.match_token(TokenType::Star) {
28060                    // {*} or {* EXCLUDE ...} or {* ILIKE ...}
28061                    // Check for ILIKE first since it's different from standard star modifiers
28062                    if self.check_keyword_text("ILIKE") {
28063                        self.skip();
28064                        let pattern = self.parse_expression()?;
28065                        // Create an ILike expression with Star as left side
28066                        Expression::ILike(Box::new(LikeOp {
28067                            left: Expression::Star(Star {
28068                                table: None,
28069                                except: None,
28070                                replace: None,
28071                                rename: None,
28072                                trailing_comments: Vec::new(),
28073                                span: None,
28074                            }),
28075                            right: pattern,
28076                            escape: None,
28077                            quantifier: None,
28078                            inferred_type: None,
28079                        }))
28080                    } else {
28081                        // {*} or {* EXCLUDE ...}
28082                        let star = self.parse_star_modifiers(None)?;
28083                        Expression::Star(star)
28084                    }
28085                } else {
28086                    // {tbl.*} - table qualified wildcard
28087                    let table_name = self.expect_identifier_or_keyword_with_quoted()?;
28088                    self.expect(TokenType::Dot)?;
28089                    self.expect(TokenType::Star)?;
28090                    let star = self.parse_star_modifiers(Some(table_name))?;
28091                    Expression::Star(star)
28092                };
28093
28094                self.expect(TokenType::RBrace)?;
28095
28096                // Wrap in BracedWildcard for generation
28097                return Ok(Expression::BracedWildcard(Box::new(wildcard_expr)));
28098            }
28099
28100            // Parse key-value pairs: key: value, ...
28101            let mut keys = Vec::new();
28102            let mut values = Vec::new();
28103            loop {
28104                let key = self.parse_expression()?;
28105                self.expect(TokenType::Colon)?;
28106                let value = self.parse_expression()?;
28107                keys.push(key);
28108                values.push(value);
28109                if !self.match_token(TokenType::Comma) {
28110                    break;
28111                }
28112                // Handle trailing comma
28113                if self.check(TokenType::RBrace) {
28114                    break;
28115                }
28116            }
28117            self.expect(TokenType::RBrace)?;
28118            return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
28119                keys,
28120                values,
28121                curly_brace_syntax: true,
28122                with_map_keyword: false,
28123            })));
28124        }
28125
28126        // Parenthesized expression or subquery
28127        if self.match_token(TokenType::LParen) {
28128            // Capture comments from the ( token (e.g., "(/* comment */ 1)")
28129            let lparen_comments = self.previous_trailing_comments().to_vec();
28130
28131            // Empty parens () — could be empty tuple or zero-param lambda () -> body
28132            if self.check(TokenType::RParen) {
28133                self.skip(); // consume )
28134                             // Check for lambda: () -> body
28135                if self.match_token(TokenType::Arrow) || self.match_token(TokenType::FArrow) {
28136                    let body = self.parse_expression()?;
28137                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
28138                        parameters: Vec::new(),
28139                        body,
28140                        colon: false,
28141                        parameter_types: Vec::new(),
28142                    })));
28143                }
28144                // Otherwise empty tuple
28145                return self.maybe_parse_subscript(Expression::Tuple(Box::new(Tuple {
28146                    expressions: Vec::new(),
28147                })));
28148            }
28149
28150            // Check if this is a VALUES expression inside parens: (VALUES ...)
28151            if self.check(TokenType::Values) {
28152                let values = self.parse_values()?;
28153                self.expect(TokenType::RParen)?;
28154                return Ok(Expression::Subquery(Box::new(Subquery {
28155                    this: values,
28156                    alias: None,
28157                    column_aliases: Vec::new(),
28158                    order_by: None,
28159                    limit: None,
28160                    offset: None,
28161                    distribute_by: None,
28162                    sort_by: None,
28163                    cluster_by: None,
28164                    lateral: false,
28165                    modifiers_inside: false,
28166                    trailing_comments: self.previous_trailing_comments().to_vec(),
28167                    inferred_type: None,
28168                })));
28169            }
28170
28171            // Check if this is a subquery (SELECT, WITH, DuckDB FROM-first, or ClickHouse EXPLAIN)
28172            let is_explain_subquery = self.check(TokenType::Var)
28173                && self.peek().text.eq_ignore_ascii_case("EXPLAIN")
28174                && self.peek_nth(1).map_or(false, |t| {
28175                    // EXPLAIN followed by statement/style keywords is a subquery
28176                    matches!(
28177                        t.token_type,
28178                        TokenType::Select
28179                            | TokenType::Insert
28180                            | TokenType::Create
28181                            | TokenType::Alter
28182                            | TokenType::Drop
28183                            | TokenType::Set
28184                            | TokenType::System
28185                            | TokenType::Table
28186                    ) || matches!(
28187                        t.text.to_ascii_uppercase().as_str(),
28188                        "SYNTAX" | "AST" | "PLAN" | "PIPELINE" | "ESTIMATE" | "CURRENT" | "QUERY"
28189                    ) || (t.token_type == TokenType::Var
28190                        && self
28191                            .peek_nth(2)
28192                            .map_or(false, |t2| t2.token_type == TokenType::Eq))
28193                });
28194            // ClickHouse: (from, to, ...) -> body is a tuple-lambda with keyword params
28195            // Detect pattern: (keyword/ident, keyword/ident, ...) ->
28196            if matches!(
28197                self.config.dialect,
28198                Some(crate::dialects::DialectType::ClickHouse)
28199            ) {
28200                let mut look = self.current;
28201                let mut is_tuple_lambda = true;
28202                let mut param_count = 0;
28203                loop {
28204                    if look >= self.tokens.len() {
28205                        is_tuple_lambda = false;
28206                        break;
28207                    }
28208                    let tt = self.tokens[look].token_type;
28209                    if tt == TokenType::Identifier
28210                        || tt == TokenType::Var
28211                        || tt == TokenType::QuotedIdentifier
28212                        || tt.is_keyword()
28213                    {
28214                        param_count += 1;
28215                        look += 1;
28216                    } else {
28217                        is_tuple_lambda = false;
28218                        break;
28219                    }
28220                    if look >= self.tokens.len() {
28221                        is_tuple_lambda = false;
28222                        break;
28223                    }
28224                    if self.tokens[look].token_type == TokenType::Comma {
28225                        look += 1;
28226                    } else if self.tokens[look].token_type == TokenType::RParen {
28227                        look += 1;
28228                        break;
28229                    } else {
28230                        is_tuple_lambda = false;
28231                        break;
28232                    }
28233                }
28234                if is_tuple_lambda
28235                    && param_count >= 1
28236                    && look < self.tokens.len()
28237                    && self.tokens[look].token_type == TokenType::Arrow
28238                {
28239                    // Parse as lambda: consume params
28240                    let mut params = Vec::new();
28241                    loop {
28242                        let tok = self.advance();
28243                        params.push(Identifier::new(tok.text));
28244                        if self.match_token(TokenType::Comma) {
28245                            continue;
28246                        }
28247                        break;
28248                    }
28249                    self.expect(TokenType::RParen)?;
28250                    self.expect(TokenType::Arrow)?;
28251                    let body = self.parse_expression()?;
28252                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
28253                        parameters: params,
28254                        body,
28255                        colon: false,
28256                        parameter_types: Vec::new(),
28257                    })));
28258                }
28259            }
28260            if self.check(TokenType::Select)
28261                || self.check(TokenType::With)
28262                || self.check(TokenType::From)
28263                || is_explain_subquery
28264            {
28265                let query = self.parse_statement()?;
28266
28267                // Parse LIMIT/OFFSET that may appear after set operations INSIDE the parentheses
28268                // e.g., (SELECT 1 EXCEPT (SELECT 2) LIMIT 1)
28269                let limit = if self.match_token(TokenType::Limit) {
28270                    Some(Limit {
28271                        this: self.parse_expression()?,
28272                        percent: false,
28273                        comments: Vec::new(),
28274                    })
28275                } else {
28276                    None
28277                };
28278                let offset = if self.match_token(TokenType::Offset) {
28279                    Some(Offset {
28280                        this: self.parse_expression()?,
28281                        rows: None,
28282                    })
28283                } else {
28284                    None
28285                };
28286
28287                self.expect(TokenType::RParen)?;
28288
28289                // Wrap in Subquery to preserve parentheses in set operations
28290                let subquery = if limit.is_some() || offset.is_some() {
28291                    // If we have limit/offset INSIDE the parens, set modifiers_inside = true
28292                    Expression::Subquery(Box::new(Subquery {
28293                        this: query,
28294                        alias: None,
28295                        column_aliases: Vec::new(),
28296                        order_by: None,
28297                        limit,
28298                        offset,
28299                        distribute_by: None,
28300                        sort_by: None,
28301                        cluster_by: None,
28302                        lateral: false,
28303                        modifiers_inside: true,
28304                        trailing_comments: self.previous_trailing_comments().to_vec(),
28305                        inferred_type: None,
28306                    }))
28307                } else {
28308                    Expression::Subquery(Box::new(Subquery {
28309                        this: query,
28310                        alias: None,
28311                        column_aliases: Vec::new(),
28312                        order_by: None,
28313                        limit: None,
28314                        offset: None,
28315                        distribute_by: None,
28316                        sort_by: None,
28317                        cluster_by: None,
28318                        lateral: false,
28319                        modifiers_inside: false,
28320                        trailing_comments: self.previous_trailing_comments().to_vec(),
28321                        inferred_type: None,
28322                    }))
28323                };
28324
28325                // Check for set operations after the subquery (e.g., (SELECT 1) UNION (SELECT 2))
28326                let set_result = self.parse_set_operation(subquery)?;
28327
28328                // Only parse ORDER BY/LIMIT/OFFSET after set operations if there WAS a set operation
28329                // (for cases like ((SELECT 0) UNION (SELECT 1) ORDER BY 1 OFFSET 1))
28330                // If there's no set operation, we should NOT consume these - they belong to outer context
28331                let had_set_operation = matches!(
28332                    &set_result,
28333                    Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
28334                );
28335
28336                let result = if had_set_operation {
28337                    let order_by = if self.check(TokenType::Order) {
28338                        self.expect(TokenType::Order)?;
28339                        self.expect(TokenType::By)?;
28340                        Some(self.parse_order_by()?)
28341                    } else {
28342                        None
28343                    };
28344                    let limit_after = if self.match_token(TokenType::Limit) {
28345                        Some(Limit {
28346                            this: self.parse_expression()?,
28347                            percent: false,
28348                            comments: Vec::new(),
28349                        })
28350                    } else {
28351                        None
28352                    };
28353                    let offset_after = if self.match_token(TokenType::Offset) {
28354                        Some(Offset {
28355                            this: self.parse_expression()?,
28356                            rows: None,
28357                        })
28358                    } else {
28359                        None
28360                    };
28361
28362                    // If we have any modifiers, wrap in a Subquery with the modifiers OUTSIDE the paren
28363                    if order_by.is_some() || limit_after.is_some() || offset_after.is_some() {
28364                        Expression::Subquery(Box::new(Subquery {
28365                            this: set_result,
28366                            alias: None,
28367                            column_aliases: Vec::new(),
28368                            order_by,
28369                            limit: limit_after,
28370                            offset: offset_after,
28371                            lateral: false,
28372                            modifiers_inside: false,
28373                            trailing_comments: Vec::new(),
28374                            distribute_by: None,
28375                            sort_by: None,
28376                            cluster_by: None,
28377                            inferred_type: None,
28378                        }))
28379                    } else {
28380                        set_result
28381                    }
28382                } else {
28383                    set_result
28384                };
28385                // Allow postfix operators on subquery expressions (e.g., (SELECT 1, 2).1 for tuple element access)
28386                return self.maybe_parse_subscript(result);
28387            }
28388
28389            // Check if this starts with another paren that might be a subquery
28390            // e.g., ((SELECT 1))
28391            if self.check(TokenType::LParen) {
28392                let expr = self.parse_expression()?;
28393
28394                // Handle aliasing of expression inside outer parens (e.g., ((a, b) AS c))
28395                let first_expr = if self.match_token(TokenType::As) {
28396                    let alias = self.expect_identifier_or_alias_keyword_with_quoted()?;
28397                    Expression::Alias(Box::new(Alias::new(expr, alias)))
28398                } else {
28399                    expr
28400                };
28401
28402                // Check for tuple of tuples: ((1, 2), (3, 4))
28403                // Also handles ClickHouse: ((SELECT 1) AS x, (SELECT 2) AS y)
28404                if self.match_token(TokenType::Comma) {
28405                    let mut expressions = vec![first_expr];
28406                    loop {
28407                        if self.check(TokenType::RParen) {
28408                            break;
28409                        } // trailing comma
28410                        let elem = self.parse_expression()?;
28411                        // Handle AS alias after each element (ClickHouse tuple CTE pattern)
28412                        let elem = if self.match_token(TokenType::As) {
28413                            let alias = self.expect_identifier_or_keyword()?;
28414                            Expression::Alias(Box::new(Alias::new(elem, Identifier::new(alias))))
28415                        } else {
28416                            elem
28417                        };
28418                        expressions.push(elem);
28419                        if !self.match_token(TokenType::Comma) {
28420                            break;
28421                        }
28422                    }
28423                    self.expect(TokenType::RParen)?;
28424                    let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
28425                    return self.maybe_parse_subscript(tuple_expr);
28426                }
28427
28428                let result = first_expr;
28429
28430                self.expect(TokenType::RParen)?;
28431                let mut nested_paren_comments = lparen_comments.clone();
28432                nested_paren_comments.extend_from_slice(self.previous_trailing_comments());
28433                // Check for set operations after parenthesized expression
28434                if self.check(TokenType::Union)
28435                    || self.check(TokenType::Intersect)
28436                    || self.check(TokenType::Except)
28437                {
28438                    // This is a set operation - need to handle specially
28439                    if let Expression::Subquery(subq) = &result {
28440                        let set_result = self.parse_set_operation(subq.this.clone())?;
28441
28442                        // Parse ORDER BY/LIMIT/OFFSET after set operations
28443                        let order_by = if self.check(TokenType::Order) {
28444                            self.expect(TokenType::Order)?;
28445                            self.expect(TokenType::By)?;
28446                            Some(self.parse_order_by()?)
28447                        } else {
28448                            None
28449                        };
28450                        let limit = if self.match_token(TokenType::Limit) {
28451                            Some(Limit {
28452                                this: self.parse_expression()?,
28453                                percent: false,
28454                                comments: Vec::new(),
28455                            })
28456                        } else {
28457                            None
28458                        };
28459                        let offset = if self.match_token(TokenType::Offset) {
28460                            Some(Offset {
28461                                this: self.parse_expression()?,
28462                                rows: None,
28463                            })
28464                        } else {
28465                            None
28466                        };
28467
28468                        return Ok(Expression::Subquery(Box::new(Subquery {
28469                            this: set_result,
28470                            alias: None,
28471                            column_aliases: Vec::new(),
28472                            order_by,
28473                            limit,
28474                            offset,
28475                            lateral: false,
28476                            modifiers_inside: false,
28477                            trailing_comments: Vec::new(),
28478                            distribute_by: None,
28479                            sort_by: None,
28480                            cluster_by: None,
28481                            inferred_type: None,
28482                        })));
28483                    }
28484                }
28485                return self.maybe_parse_over(Expression::Paren(Box::new(Paren {
28486                    this: result,
28487                    trailing_comments: nested_paren_comments,
28488                })));
28489            }
28490
28491            let expr = self.parse_expression()?;
28492
28493            // Check for AS alias on the first element (e.g., (x AS y, ...))
28494            let first_expr = if self.match_token(TokenType::As) {
28495                let alias = self.expect_identifier_or_keyword_with_quoted()?;
28496                Expression::Alias(Box::new(Alias::new(expr, alias)))
28497            } else {
28498                expr
28499            };
28500
28501            // Check for tuple (multiple expressions separated by commas)
28502            if self.match_token(TokenType::Comma) {
28503                let mut expressions = vec![first_expr];
28504                // ClickHouse: trailing comma creates single-element tuple, e.g., (1,)
28505                if self.check(TokenType::RParen) {
28506                    self.skip(); // consume )
28507                    let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
28508                    return self.maybe_parse_subscript(tuple_expr);
28509                }
28510                // Parse remaining tuple elements, each can have AS alias
28511                loop {
28512                    let elem = self.parse_expression()?;
28513                    let elem_with_alias = if self.match_token(TokenType::As) {
28514                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
28515                        Expression::Alias(Box::new(Alias::new(elem, alias)))
28516                    } else {
28517                        elem
28518                    };
28519                    expressions.push(elem_with_alias);
28520                    if !self.match_token(TokenType::Comma) {
28521                        break;
28522                    }
28523                    // ClickHouse: trailing comma in multi-element tuple, e.g., (1, 2,)
28524                    if self.check(TokenType::RParen) {
28525                        break;
28526                    }
28527                }
28528
28529                self.expect(TokenType::RParen)?;
28530
28531                // Check for lambda expression: (a, b) -> body
28532                if self.match_token(TokenType::Arrow) {
28533                    let parameters = expressions
28534                        .into_iter()
28535                        .filter_map(|e| {
28536                            if let Expression::Column(c) = e {
28537                                Some(c.name)
28538                            } else if let Expression::Identifier(id) = e {
28539                                Some(id)
28540                            } else {
28541                                None
28542                            }
28543                        })
28544                        .collect();
28545                    let body = self.parse_expression()?;
28546                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
28547                        parameters,
28548                        body,
28549                        colon: false,
28550                        parameter_types: Vec::new(),
28551                    })));
28552                }
28553
28554                // Check for optional alias on the whole tuple
28555                // But NOT when AS is followed by a type constructor like Tuple(a Int8, ...)
28556                // or STRUCT<a TINYINT, ...> which would be part of a CAST expression: CAST((1, 2) AS Tuple(a Int8, b Int16))
28557                // Also NOT when AS is followed by a type name then ) like: CAST((1, 2) AS String)
28558                let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
28559                let result = if self.check(TokenType::As) {
28560                    // Look ahead: AS + type_keyword + ( or < → likely a type, not an alias
28561                    let after_as = self.current + 1;
28562                    let after_ident = self.current + 2;
28563                    let is_type_constructor = after_ident < self.tokens.len()
28564                        && (self.tokens[after_as].token_type == TokenType::Identifier
28565                            || self.tokens[after_as].token_type == TokenType::Var
28566                            || self.tokens[after_as].token_type == TokenType::Nullable
28567                            || self.tokens[after_as].token_type == TokenType::Struct
28568                            || self.tokens[after_as].token_type == TokenType::Array)
28569                        && (self.tokens[after_ident].token_type == TokenType::LParen
28570                            || self.tokens[after_ident].token_type == TokenType::Lt);
28571                    // Check if AS is followed by identifier/keyword then ), indicating CAST(tuple AS Type)
28572                    let is_cast_type = after_ident < self.tokens.len()
28573                        && (self.tokens[after_as].token_type == TokenType::Identifier
28574                            || self.tokens[after_as].token_type == TokenType::Var
28575                            || self.tokens[after_as].token_type.is_keyword())
28576                        && self.tokens[after_ident].token_type == TokenType::RParen;
28577                    if is_type_constructor || is_cast_type {
28578                        tuple_expr
28579                    } else {
28580                        self.skip(); // consume AS
28581                        let alias = self.expect_identifier()?;
28582                        Expression::Alias(Box::new(Alias::new(tuple_expr, Identifier::new(alias))))
28583                    }
28584                } else {
28585                    tuple_expr
28586                };
28587
28588                // Allow postfix operators on tuple expressions (e.g., ('a', 'b').1 for tuple element access)
28589                return self.maybe_parse_subscript(result);
28590            }
28591
28592            // ClickHouse: (x -> body) — lambda inside parentheses
28593            if matches!(
28594                self.config.dialect,
28595                Some(crate::dialects::DialectType::ClickHouse)
28596            ) && self.match_token(TokenType::Arrow)
28597            {
28598                let parameters = if let Expression::Column(c) = first_expr {
28599                    vec![c.name]
28600                } else if let Expression::Identifier(id) = first_expr {
28601                    vec![id]
28602                } else {
28603                    return Err(self.parse_error("Expected identifier as lambda parameter"));
28604                };
28605                let body = self.parse_expression()?;
28606                self.expect(TokenType::RParen)?;
28607                return Ok(Expression::Paren(Box::new(Paren {
28608                    this: Expression::Lambda(Box::new(LambdaExpr {
28609                        parameters,
28610                        body,
28611                        colon: false,
28612                        parameter_types: Vec::new(),
28613                    })),
28614                    trailing_comments: Vec::new(),
28615                })));
28616            }
28617
28618            self.expect(TokenType::RParen)?;
28619            // Combine comments from ( and ) tokens
28620            let mut paren_comments = lparen_comments.clone();
28621            paren_comments.extend_from_slice(self.previous_trailing_comments());
28622
28623            // Check for lambda expression: (x) -> body or single identifier case
28624            if self.match_token(TokenType::Arrow) {
28625                // first_expr should be a single identifier for the parameter
28626                let parameters = if let Expression::Column(c) = first_expr {
28627                    vec![c.name]
28628                } else if let Expression::Identifier(id) = first_expr {
28629                    vec![id]
28630                } else {
28631                    return Err(self.parse_error("Expected identifier as lambda parameter"));
28632                };
28633                let body = self.parse_expression()?;
28634                return Ok(Expression::Lambda(Box::new(LambdaExpr {
28635                    parameters,
28636                    body,
28637                    colon: false,
28638                    parameter_types: Vec::new(),
28639                })));
28640            }
28641
28642            return self.maybe_parse_over(Expression::Paren(Box::new(Paren {
28643                this: first_expr,
28644                trailing_comments: paren_comments,
28645            })));
28646        }
28647
28648        // NULL
28649        if self.match_token(TokenType::Null) {
28650            return Ok(Expression::Null(Null));
28651        }
28652
28653        // TRUE
28654        if self.match_token(TokenType::True) {
28655            return Ok(Expression::Boolean(BooleanLiteral { value: true }));
28656        }
28657
28658        // FALSE
28659        if self.match_token(TokenType::False) {
28660            return Ok(Expression::Boolean(BooleanLiteral { value: false }));
28661        }
28662
28663        // LAMBDA expression (DuckDB syntax: LAMBDA x : expr)
28664        if self.check(TokenType::Lambda) {
28665            if let Some(lambda) = self.parse_lambda()? {
28666                return Ok(lambda);
28667            }
28668        }
28669
28670        // CASE expression - but not if followed by DOT (then it's an identifier like case.column)
28671        if self.check(TokenType::Case) && !self.check_next(TokenType::Dot) {
28672            let case_expr = self.parse_case()?;
28673            return self.maybe_parse_over(case_expr);
28674        }
28675
28676        // CAST expression
28677        if self.check(TokenType::Cast) {
28678            let cast_expr = self.parse_cast()?;
28679            return self.maybe_parse_subscript(cast_expr);
28680        }
28681
28682        // TRY_CAST expression
28683        if self.check(TokenType::TryCast) {
28684            let cast_expr = self.parse_try_cast()?;
28685            return self.maybe_parse_subscript(cast_expr);
28686        }
28687
28688        // SAFE_CAST expression (BigQuery)
28689        if self.check(TokenType::SafeCast) {
28690            let cast_expr = self.parse_safe_cast()?;
28691            return self.maybe_parse_subscript(cast_expr);
28692        }
28693
28694        // EXISTS - either subquery predicate EXISTS(SELECT ...) or Hive array function EXISTS(array, lambda)
28695        // ClickHouse: EXISTS without ( is a column name/identifier
28696        if self.check(TokenType::Exists)
28697            && matches!(
28698                self.config.dialect,
28699                Some(crate::dialects::DialectType::ClickHouse)
28700            )
28701            && !self.check_next(TokenType::LParen)
28702        {
28703            let tok = self.advance();
28704            return Ok(Expression::Identifier(Identifier::new(tok.text)));
28705        }
28706        if self.match_token(TokenType::Exists) {
28707            self.expect(TokenType::LParen)?;
28708
28709            // Check if this is a subquery EXISTS (SELECT, WITH, or FROM for DuckDB)
28710            // ClickHouse: also handle EXISTS((SELECT ...)) with double parens
28711            if self.check(TokenType::Select)
28712                || self.check(TokenType::With)
28713                || self.check(TokenType::From)
28714                || (self.check(TokenType::LParen)
28715                    && self
28716                        .peek_nth(1)
28717                        .map(|t| {
28718                            matches!(
28719                                t.token_type,
28720                                TokenType::Select | TokenType::With | TokenType::From
28721                            )
28722                        })
28723                        .unwrap_or(false))
28724            {
28725                let query = self.parse_statement()?;
28726                self.expect(TokenType::RParen)?;
28727                return Ok(Expression::Exists(Box::new(Exists {
28728                    this: query,
28729                    not: false,
28730                })));
28731            }
28732
28733            // Otherwise it's Hive's array EXISTS function: EXISTS(array, lambda_predicate)
28734            // This function checks if any element in the array matches the predicate
28735            let array_expr = self.parse_expression()?;
28736            self.expect(TokenType::Comma)?;
28737            let predicate = self.parse_expression()?;
28738            self.expect(TokenType::RParen)?;
28739            return Ok(Expression::Function(Box::new(Function {
28740                name: "EXISTS".to_string(),
28741                args: vec![array_expr, predicate],
28742                distinct: false,
28743                trailing_comments: Vec::new(),
28744                use_bracket_syntax: false,
28745                no_parens: false,
28746                quoted: false,
28747                span: None,
28748                inferred_type: None,
28749            })));
28750        }
28751
28752        // INTERVAL expression or identifier
28753        if self.check(TokenType::Interval) {
28754            if let Some(interval_expr) = self.try_parse_interval()? {
28755                return Ok(interval_expr);
28756            }
28757            // INTERVAL is used as an identifier
28758            let token = self.advance();
28759            return Ok(Expression::Identifier(Identifier::new(token.text)));
28760        }
28761
28762        // DATE literal: DATE '2024-01-15' or DATE function: DATE(expr)
28763        if self.check(TokenType::Date) {
28764            let token = self.advance();
28765            let original_text = token.text.clone();
28766            if self.check(TokenType::String) {
28767                let str_token = self.advance();
28768                if self.config.dialect.is_none() {
28769                    // Generic (no dialect): DATE 'literal' -> CAST('literal' AS DATE)
28770                    return Ok(Expression::Cast(Box::new(Cast {
28771                        this: Expression::Literal(Box::new(Literal::String(str_token.text))),
28772                        to: DataType::Date,
28773                        trailing_comments: Vec::new(),
28774                        double_colon_syntax: false,
28775                        format: None,
28776                        default: None,
28777                        inferred_type: None,
28778                    })));
28779                }
28780                return Ok(Expression::Literal(Box::new(Literal::Date(str_token.text))));
28781            }
28782            // Check for DATE() function call
28783            if self.match_token(TokenType::LParen) {
28784                let func_expr = self.parse_typed_function(&original_text, "DATE", false)?;
28785                return self.maybe_parse_over(func_expr);
28786            }
28787            // Fallback to DATE as column reference - preserve original case
28788            return Ok(Expression::boxed_column(Column {
28789                name: Identifier::new(original_text),
28790                table: None,
28791                join_mark: false,
28792                trailing_comments: Vec::new(),
28793                span: None,
28794                inferred_type: None,
28795            }));
28796        }
28797
28798        // TIME literal: TIME '10:30:00' or TIME function: TIME(expr)
28799        if self.check(TokenType::Time) {
28800            let token = self.advance();
28801            let original_text = token.text.clone();
28802            if self.check(TokenType::String) {
28803                let str_token = self.advance();
28804                return Ok(Expression::Literal(Box::new(Literal::Time(str_token.text))));
28805            }
28806            // Check for TIME() function call
28807            if self.match_token(TokenType::LParen) {
28808                let func_expr = self.parse_typed_function(&original_text, "TIME", false)?;
28809                return self.maybe_parse_over(func_expr);
28810            }
28811            // Fallback to TIME as column reference - preserve original case
28812            return self.maybe_parse_subscript(Expression::boxed_column(Column {
28813                name: Identifier::new(original_text),
28814                table: None,
28815                join_mark: false,
28816                trailing_comments: Vec::new(),
28817                span: None,
28818                inferred_type: None,
28819            }));
28820        }
28821
28822        // TIMESTAMP literal: TIMESTAMP '2024-01-15 10:30:00' or TIMESTAMP function: TIMESTAMP(expr)
28823        // Also handles TIMESTAMP(n) WITH TIME ZONE as a data type expression
28824        if self.check(TokenType::Timestamp) {
28825            let token = self.advance();
28826            let original_text = token.text.clone();
28827            if self.check(TokenType::String) {
28828                let str_token = self.advance();
28829                if self.config.dialect.is_none() {
28830                    // Generic (no dialect): TIMESTAMP 'literal' -> CAST('literal' AS TIMESTAMP)
28831                    return Ok(Expression::Cast(Box::new(Cast {
28832                        this: Expression::Literal(Box::new(Literal::String(str_token.text))),
28833                        to: DataType::Timestamp {
28834                            precision: None,
28835                            timezone: false,
28836                        },
28837                        trailing_comments: Vec::new(),
28838                        double_colon_syntax: false,
28839                        format: None,
28840                        default: None,
28841                        inferred_type: None,
28842                    })));
28843                }
28844                // Dialect-specific: keep as Literal::Timestamp for dialect transforms
28845                return Ok(Expression::Literal(Box::new(Literal::Timestamp(
28846                    str_token.text,
28847                ))));
28848            }
28849            // Check for TIMESTAMP(n) WITH/WITHOUT TIME ZONE or TIMESTAMP(n) 'literal' as data type
28850            // This is a data type, not a function call
28851            if self.check(TokenType::LParen) {
28852                // Look ahead to see if this is TIMESTAMP(number) WITH/WITHOUT/String (data type)
28853                // vs TIMESTAMP(expr) (function call)
28854                let is_data_type = self.check_next(TokenType::Number) && {
28855                    // Check if after (number) there's WITH, WITHOUT, or String literal
28856                    let mut lookahead = self.current + 2;
28857                    // Skip the number
28858                    while lookahead < self.tokens.len()
28859                        && self.tokens[lookahead].token_type == TokenType::RParen
28860                    {
28861                        lookahead += 1;
28862                        break;
28863                    }
28864                    // Check for WITH, WITHOUT, or String after the closing paren
28865                    lookahead < self.tokens.len()
28866                        && (self.tokens[lookahead].token_type == TokenType::With
28867                            || self.tokens[lookahead].text.eq_ignore_ascii_case("WITHOUT")
28868                            || self.tokens[lookahead].token_type == TokenType::String)
28869                };
28870
28871                if is_data_type {
28872                    // Parse as data type: TIMESTAMP(precision) [WITH/WITHOUT TIME ZONE] ['literal']
28873                    self.skip(); // consume (
28874                    let precision = Some(self.expect_number()? as u32);
28875                    self.expect(TokenType::RParen)?;
28876
28877                    let data_type = if self.match_token(TokenType::With) {
28878                        if self.match_token(TokenType::Local) {
28879                            // WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
28880                            self.match_keyword("TIME");
28881                            self.match_keyword("ZONE");
28882                            DataType::Custom {
28883                                name: format!("TIMESTAMPLTZ({})", precision.unwrap()),
28884                            }
28885                        } else {
28886                            self.match_keyword("TIME");
28887                            self.match_keyword("ZONE");
28888                            DataType::Timestamp {
28889                                precision,
28890                                timezone: true,
28891                            }
28892                        }
28893                    } else if self.match_keyword("WITHOUT") {
28894                        self.match_keyword("TIME");
28895                        self.match_keyword("ZONE");
28896                        DataType::Timestamp {
28897                            precision,
28898                            timezone: false,
28899                        }
28900                    } else {
28901                        DataType::Timestamp {
28902                            precision,
28903                            timezone: false,
28904                        }
28905                    };
28906
28907                    // Check for following string literal -> wrap in CAST
28908                    if self.check(TokenType::String) {
28909                        let str_token = self.advance();
28910                        return Ok(Expression::Cast(Box::new(Cast {
28911                            this: Expression::Literal(Box::new(Literal::String(str_token.text))),
28912                            to: data_type,
28913                            trailing_comments: Vec::new(),
28914                            double_colon_syntax: false,
28915                            format: None,
28916                            default: None,
28917                            inferred_type: None,
28918                        })));
28919                    }
28920
28921                    return Ok(Expression::DataType(data_type));
28922                }
28923
28924                // Otherwise parse as function call
28925                self.skip(); // consume (
28926                let func_expr = self.parse_typed_function(&original_text, "TIMESTAMP", false)?;
28927                return self.maybe_parse_over(func_expr);
28928            }
28929            // Check for TIMESTAMP WITH/WITHOUT TIME ZONE (no precision) as data type
28930            // Use lookahead to verify WITH is followed by TIME (not WITH FILL, WITH TOTALS, etc.)
28931            if (self.check(TokenType::With)
28932                && self.peek_nth(1).map_or(false, |t| {
28933                    t.text.eq_ignore_ascii_case("TIME") || t.text.eq_ignore_ascii_case("LOCAL")
28934                }))
28935                || self.check_keyword_text("WITHOUT")
28936            {
28937                let data_type = if self.match_token(TokenType::With) {
28938                    if self.match_token(TokenType::Local) {
28939                        // WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
28940                        self.match_keyword("TIME");
28941                        self.match_keyword("ZONE");
28942                        DataType::Custom {
28943                            name: "TIMESTAMPLTZ".to_string(),
28944                        }
28945                    } else {
28946                        self.match_keyword("TIME");
28947                        self.match_keyword("ZONE");
28948                        DataType::Timestamp {
28949                            precision: None,
28950                            timezone: true,
28951                        }
28952                    }
28953                } else if self.match_keyword("WITHOUT") {
28954                    self.match_keyword("TIME");
28955                    self.match_keyword("ZONE");
28956                    DataType::Timestamp {
28957                        precision: None,
28958                        timezone: false,
28959                    }
28960                } else {
28961                    DataType::Timestamp {
28962                        precision: None,
28963                        timezone: false,
28964                    }
28965                };
28966
28967                // Check for following string literal -> wrap in CAST
28968                if self.check(TokenType::String) {
28969                    let str_token = self.advance();
28970                    return Ok(Expression::Cast(Box::new(Cast {
28971                        this: Expression::Literal(Box::new(Literal::String(str_token.text))),
28972                        to: data_type,
28973                        trailing_comments: Vec::new(),
28974                        double_colon_syntax: false,
28975                        format: None,
28976                        default: None,
28977                        inferred_type: None,
28978                    })));
28979                }
28980
28981                return Ok(Expression::DataType(data_type));
28982            }
28983            // Fallback to TIMESTAMP as column reference - preserve original case
28984            return Ok(Expression::boxed_column(Column {
28985                name: Identifier::new(original_text),
28986                table: None,
28987                join_mark: false,
28988                trailing_comments: Vec::new(),
28989                span: None,
28990                inferred_type: None,
28991            }));
28992        }
28993
28994        // DATETIME literal: DATETIME '2024-01-15 10:30:00' or DATETIME function: DATETIME(expr)
28995        if self.check(TokenType::DateTime) {
28996            let token = self.advance();
28997            let original_text = token.text.clone();
28998            if self.check(TokenType::String) {
28999                let str_token = self.advance();
29000                return Ok(Expression::Literal(Box::new(Literal::Datetime(
29001                    str_token.text,
29002                ))));
29003            }
29004            // Check for DATETIME() function call
29005            if self.match_token(TokenType::LParen) {
29006                let func_expr = self.parse_typed_function(&original_text, "DATETIME", false)?;
29007                return self.maybe_parse_over(func_expr);
29008            }
29009            // Fallback to DATETIME as column reference - preserve original case
29010            return Ok(Expression::boxed_column(Column {
29011                name: Identifier::new(original_text),
29012                table: None,
29013                join_mark: false,
29014                trailing_comments: Vec::new(),
29015                span: None,
29016                inferred_type: None,
29017            }));
29018        }
29019
29020        // ROW() function (window function for row number)
29021        if self.check(TokenType::Row) && self.check_next(TokenType::LParen) {
29022            self.skip(); // consume ROW
29023            self.expect(TokenType::LParen)?;
29024            // ROW() typically takes no arguments
29025            let args = if !self.check(TokenType::RParen) {
29026                self.parse_expression_list()?
29027            } else {
29028                Vec::new()
29029            };
29030            self.expect(TokenType::RParen)?;
29031            let func_expr = Expression::Function(Box::new(Function {
29032                name: "ROW".to_string(),
29033                args,
29034                distinct: false,
29035                trailing_comments: Vec::new(),
29036                use_bracket_syntax: false,
29037                no_parens: false,
29038                quoted: false,
29039                span: None,
29040                inferred_type: None,
29041            }));
29042            return self.maybe_parse_over(func_expr);
29043        }
29044
29045        // Number - support postfix operators like ::type
29046        if self.check(TokenType::Number) {
29047            let token = self.advance();
29048            if matches!(
29049                self.config.dialect,
29050                Some(crate::dialects::DialectType::MySQL)
29051            ) {
29052                let text = token.text.as_str();
29053                if text.len() > 2
29054                    && (text.starts_with("0x") || text.starts_with("0X"))
29055                    && !text[2..].chars().all(|c| c.is_ascii_hexdigit())
29056                {
29057                    let ident = Expression::Identifier(Identifier {
29058                        name: token.text,
29059                        quoted: true,
29060                        trailing_comments: Vec::new(),
29061                        span: None,
29062                    });
29063                    return self.maybe_parse_subscript(ident);
29064                }
29065            }
29066            if matches!(
29067                self.config.dialect,
29068                Some(crate::dialects::DialectType::Teradata)
29069            ) && token.text == "0"
29070            {
29071                if let Some(next) = self.tokens.get(self.current) {
29072                    let is_adjacent = token.span.end == next.span.start;
29073                    let next_text = next.text.as_str();
29074                    let is_hex_prefix = next_text.starts_with('x') || next_text.starts_with('X');
29075                    if is_adjacent
29076                        && matches!(next.token_type, TokenType::Identifier | TokenType::Var)
29077                        && is_hex_prefix
29078                        && next_text.len() > 1
29079                        && next_text[1..].chars().all(|c| c.is_ascii_hexdigit())
29080                    {
29081                        // Consume the hex suffix token and emit a HexString literal
29082                        let hex_token = self.advance();
29083                        let hex = hex_token.text[1..].to_string();
29084                        let literal = Expression::Literal(Box::new(Literal::HexString(hex)));
29085                        return self.maybe_parse_subscript(literal);
29086                    }
29087                }
29088            }
29089            if matches!(
29090                self.config.dialect,
29091                Some(crate::dialects::DialectType::ClickHouse)
29092            ) {
29093                if let Some(next) = self.tokens.get(self.current) {
29094                    let is_adjacent = token.span.end == next.span.start;
29095                    if is_adjacent
29096                        && matches!(next.token_type, TokenType::Identifier | TokenType::Var)
29097                        && next.text.starts_with('_')
29098                    {
29099                        let suffix = next.text.clone();
29100                        self.skip(); // consume suffix token
29101                        let combined = format!("{}{}", token.text, suffix);
29102                        let literal = Expression::Literal(Box::new(Literal::Number(combined)));
29103                        return self.maybe_parse_subscript(literal);
29104                    }
29105                }
29106            }
29107            // Check for numeric literal suffix encoded as "number::TYPE" by tokenizer
29108            let literal = if let Some(sep_pos) = token.text.find("::") {
29109                let num_part = &token.text[..sep_pos];
29110                let type_name = &token.text[sep_pos + 2..];
29111                let num_expr = Expression::Literal(Box::new(Literal::Number(num_part.to_string())));
29112                let data_type = match type_name {
29113                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
29114                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
29115                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
29116                    "DOUBLE" => crate::expressions::DataType::Double {
29117                        precision: None,
29118                        scale: None,
29119                    },
29120                    "FLOAT" => crate::expressions::DataType::Float {
29121                        precision: None,
29122                        scale: None,
29123                        real_spelling: false,
29124                    },
29125                    "DECIMAL" => crate::expressions::DataType::Decimal {
29126                        precision: None,
29127                        scale: None,
29128                    },
29129                    _ => crate::expressions::DataType::Custom {
29130                        name: type_name.to_string(),
29131                    },
29132                };
29133                Expression::Cast(Box::new(crate::expressions::Cast {
29134                    this: num_expr,
29135                    to: data_type,
29136                    trailing_comments: Vec::new(),
29137                    double_colon_syntax: false,
29138                    format: None,
29139                    default: None,
29140                    inferred_type: None,
29141                }))
29142            } else {
29143                Expression::Literal(Box::new(Literal::Number(token.text)))
29144            };
29145            return self.maybe_parse_subscript(literal);
29146        }
29147
29148        // String - support postfix operators like ::type, ->, ->>
29149        // Also handle adjacent string literals (SQL standard) which concatenate: 'x' 'y' 'z' -> CONCAT('x', 'y', 'z')
29150        if self.check(TokenType::String) {
29151            let token = self.advance();
29152            let first_literal = Expression::Literal(Box::new(Literal::String(token.text)));
29153
29154            // Check for adjacent string literals (PostgreSQL and SQL standard feature)
29155            // 'x' 'y' 'z' should be treated as string concatenation
29156            if self.check(TokenType::String) {
29157                let mut expressions = vec![first_literal];
29158                while self.check(TokenType::String) {
29159                    let next_token = self.advance();
29160                    expressions.push(Expression::Literal(Box::new(Literal::String(
29161                        next_token.text,
29162                    ))));
29163                }
29164                // Create CONCAT function call with all adjacent strings
29165                let concat_func =
29166                    Expression::Function(Box::new(Function::new("CONCAT", expressions)));
29167                return self.maybe_parse_subscript(concat_func);
29168            }
29169
29170            return self.maybe_parse_subscript(first_literal);
29171        }
29172
29173        // Dollar-quoted string: $$...$$ or $tag$...$tag$ -- preserve as DollarString
29174        // so the generator can handle dialect-specific conversion
29175        if self.check(TokenType::DollarString) {
29176            let token = self.advance();
29177            let literal = Expression::Literal(Box::new(Literal::DollarString(token.text)));
29178            return self.maybe_parse_subscript(literal);
29179        }
29180
29181        // Triple-quoted string with double quotes: """..."""
29182        if self.check(TokenType::TripleDoubleQuotedString) {
29183            let token = self.advance();
29184            let literal =
29185                Expression::Literal(Box::new(Literal::TripleQuotedString(token.text, '"')));
29186            return self.maybe_parse_subscript(literal);
29187        }
29188
29189        // Triple-quoted string with single quotes: '''...'''
29190        if self.check(TokenType::TripleSingleQuotedString) {
29191            let token = self.advance();
29192            let literal =
29193                Expression::Literal(Box::new(Literal::TripleQuotedString(token.text, '\'')));
29194            return self.maybe_parse_subscript(literal);
29195        }
29196
29197        // National String (N'...')
29198        if self.check(TokenType::NationalString) {
29199            let token = self.advance();
29200            let literal = Expression::Literal(Box::new(Literal::NationalString(token.text)));
29201            return self.maybe_parse_subscript(literal);
29202        }
29203
29204        // Hex String (X'...')
29205        if self.check(TokenType::HexString) {
29206            let token = self.advance();
29207            let literal = Expression::Literal(Box::new(Literal::HexString(token.text)));
29208            return self.maybe_parse_subscript(literal);
29209        }
29210
29211        // Hex Number (0xA from BigQuery/SQLite) - integer in hex notation
29212        if self.check(TokenType::HexNumber) {
29213            let token = self.advance();
29214            if matches!(
29215                self.config.dialect,
29216                Some(crate::dialects::DialectType::MySQL)
29217            ) {
29218                let text = token.text.as_str();
29219                if text.len() > 2
29220                    && (text.starts_with("0x") || text.starts_with("0X"))
29221                    && !text[2..].chars().all(|c| c.is_ascii_hexdigit())
29222                {
29223                    let ident = Expression::Identifier(Identifier {
29224                        name: token.text,
29225                        quoted: true,
29226                        trailing_comments: Vec::new(),
29227                        span: None,
29228                    });
29229                    return self.maybe_parse_subscript(ident);
29230                }
29231            }
29232            let literal = Expression::Literal(Box::new(Literal::HexNumber(token.text)));
29233            return self.maybe_parse_subscript(literal);
29234        }
29235
29236        // Bit String (B'...')
29237        if self.check(TokenType::BitString) {
29238            let token = self.advance();
29239            let literal = Expression::Literal(Box::new(Literal::BitString(token.text)));
29240            return self.maybe_parse_subscript(literal);
29241        }
29242
29243        // Byte String (b"..." - BigQuery style)
29244        if self.check(TokenType::ByteString) {
29245            let token = self.advance();
29246            let literal = Expression::Literal(Box::new(Literal::ByteString(token.text)));
29247            return self.maybe_parse_subscript(literal);
29248        }
29249
29250        // Raw String (r"..." - BigQuery style, backslashes are literal)
29251        if self.check(TokenType::RawString) {
29252            let token = self.advance();
29253            // Raw strings preserve backslashes as literal characters.
29254            // The generator will handle escaping when converting to a regular string.
29255            let literal = Expression::Literal(Box::new(Literal::RawString(token.text)));
29256            return self.maybe_parse_subscript(literal);
29257        }
29258
29259        // Escape String (E'...' - PostgreSQL)
29260        if self.check(TokenType::EscapeString) {
29261            let token = self.advance();
29262            // EscapeString is stored as "E'content'" - extract just the content
29263            let literal = Expression::Literal(Box::new(Literal::EscapeString(token.text)));
29264            return self.maybe_parse_subscript(literal);
29265        }
29266
29267        // Star - check for DuckDB *COLUMNS(...) syntax first
29268        if self.check(TokenType::Star) {
29269            // DuckDB *COLUMNS(...) syntax: *COLUMNS(*), *COLUMNS('regex'), *COLUMNS(['col1', 'col2'])
29270            // Check if * is followed by COLUMNS and (
29271            if self.check_next_identifier("COLUMNS") {
29272                // Check if there's a ( after COLUMNS
29273                if self
29274                    .tokens
29275                    .get(self.current + 2)
29276                    .map(|t| t.token_type == TokenType::LParen)
29277                    .unwrap_or(false)
29278                {
29279                    self.skip(); // consume *
29280                    self.skip(); // consume COLUMNS
29281                    self.skip(); // consume (
29282
29283                    // Parse the argument: can be *, a regex string, or an array of column names
29284                    let arg = if self.check(TokenType::Star) {
29285                        self.skip(); // consume *
29286                        Expression::Star(Star {
29287                            table: None,
29288                            except: None,
29289                            replace: None,
29290                            rename: None,
29291                            trailing_comments: Vec::new(),
29292                            span: None,
29293                        })
29294                    } else {
29295                        self.parse_expression()?
29296                    };
29297
29298                    self.expect(TokenType::RParen)?;
29299
29300                    // Create Columns expression with unpack=true
29301                    return Ok(Expression::Columns(Box::new(Columns {
29302                        this: Box::new(arg),
29303                        unpack: Some(Box::new(Expression::Boolean(BooleanLiteral {
29304                            value: true,
29305                        }))),
29306                    })));
29307                }
29308            }
29309
29310            // Regular star
29311            self.skip(); // consume *
29312            let star = self.parse_star_modifiers(None)?;
29313            return Ok(Expression::Star(star));
29314        }
29315
29316        // Generic type expressions: ARRAY<T>, MAP<K,V>, STRUCT<...>
29317        // These are standalone type expressions (not in CAST context)
29318        // But also handle STRUCT<TYPE>(args) which becomes CAST(STRUCT(args) AS STRUCT<TYPE>)
29319        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
29320            let name_upper = self.peek().text.to_ascii_uppercase();
29321            if (name_upper == "ARRAY" || name_upper == "MAP" || name_upper == "STRUCT")
29322                && self.check_next(TokenType::Lt)
29323            {
29324                self.skip(); // consume ARRAY/MAP/STRUCT
29325                let data_type = self.parse_data_type_from_name(&name_upper)?;
29326
29327                // Check for typed constructor: STRUCT<TYPE>(args) or ARRAY<TYPE>(args)
29328                // These become CAST(STRUCT(args) AS TYPE) or CAST(ARRAY(args) AS TYPE)
29329                if self.match_token(TokenType::LParen) {
29330                    if name_upper == "STRUCT" {
29331                        // Parse struct constructor arguments
29332                        let args = if self.check(TokenType::RParen) {
29333                            Vec::new()
29334                        } else {
29335                            self.parse_struct_args()?
29336                        };
29337                        self.expect(TokenType::RParen)?;
29338
29339                        // Convert args to Struct fields (all unnamed)
29340                        let fields: Vec<(Option<String>, Expression)> =
29341                            args.into_iter().map(|e| (None, e)).collect();
29342
29343                        // Create CAST(STRUCT(args) AS STRUCT<TYPE>)
29344                        let struct_expr = Expression::Struct(Box::new(Struct { fields }));
29345                        let cast_expr = Expression::Cast(Box::new(Cast {
29346                            this: struct_expr,
29347                            to: data_type,
29348                            trailing_comments: Vec::new(),
29349                            double_colon_syntax: false,
29350                            format: None,
29351                            default: None,
29352                            inferred_type: None,
29353                        }));
29354                        return self.maybe_parse_subscript(cast_expr);
29355                    } else if name_upper == "ARRAY" {
29356                        // Parse array constructor arguments
29357                        let mut expressions = Vec::new();
29358                        if !self.check(TokenType::RParen) {
29359                            loop {
29360                                expressions.push(self.parse_expression()?);
29361                                if !self.match_token(TokenType::Comma) {
29362                                    break;
29363                                }
29364                            }
29365                        }
29366                        self.expect(TokenType::RParen)?;
29367
29368                        // Create CAST(ARRAY[args] AS ARRAY<TYPE>)
29369                        let array_expr = Expression::Array(Box::new(Array { expressions }));
29370                        let cast_expr = Expression::Cast(Box::new(Cast {
29371                            this: array_expr,
29372                            to: data_type,
29373                            trailing_comments: Vec::new(),
29374                            double_colon_syntax: false,
29375                            format: None,
29376                            default: None,
29377                            inferred_type: None,
29378                        }));
29379                        return self.maybe_parse_subscript(cast_expr);
29380                    }
29381                } else if self.match_token(TokenType::LBracket) {
29382                    // ARRAY<TYPE>[values] or ARRAY<TYPE>[] - bracket-style array constructor
29383                    let expressions = if self.check(TokenType::RBracket) {
29384                        Vec::new()
29385                    } else {
29386                        self.parse_expression_list()?
29387                    };
29388                    self.expect(TokenType::RBracket)?;
29389                    // Create CAST(Array(values) AS DataType)
29390                    let array_expr = Expression::Array(Box::new(Array { expressions }));
29391                    let cast_expr = Expression::Cast(Box::new(Cast {
29392                        this: array_expr,
29393                        to: data_type,
29394                        trailing_comments: Vec::new(),
29395                        double_colon_syntax: false,
29396                        format: None,
29397                        default: None,
29398                        inferred_type: None,
29399                    }));
29400                    return self.maybe_parse_subscript(cast_expr);
29401                }
29402
29403                return Ok(Expression::DataType(data_type));
29404            }
29405            // DuckDB-style MAP with curly brace literals: MAP {'key': value}
29406            if name_upper == "MAP" && self.check_next(TokenType::LBrace) {
29407                self.skip(); // consume MAP
29408                self.expect(TokenType::LBrace)?;
29409
29410                // Handle empty: MAP {}
29411                if self.match_token(TokenType::RBrace) {
29412                    return self.maybe_parse_subscript(Expression::MapFunc(Box::new(
29413                        MapConstructor {
29414                            keys: Vec::new(),
29415                            values: Vec::new(),
29416                            curly_brace_syntax: true,
29417                            with_map_keyword: true,
29418                        },
29419                    )));
29420                }
29421
29422                // Parse key-value pairs
29423                let mut keys = Vec::new();
29424                let mut values = Vec::new();
29425                loop {
29426                    let key = self.parse_primary()?;
29427                    self.expect(TokenType::Colon)?;
29428                    let value = self.parse_expression()?;
29429                    keys.push(key);
29430                    values.push(value);
29431                    if !self.match_token(TokenType::Comma) {
29432                        break;
29433                    }
29434                    // Handle trailing comma
29435                    if self.check(TokenType::RBrace) {
29436                        break;
29437                    }
29438                }
29439                self.expect(TokenType::RBrace)?;
29440
29441                return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
29442                    keys,
29443                    values,
29444                    curly_brace_syntax: true,
29445                    with_map_keyword: true,
29446                })));
29447            }
29448        }
29449
29450        // Keywords as identifiers when followed by DOT (e.g., case.x, top.y)
29451        // These keywords can be table/column names when used with dot notation
29452        if (self.check(TokenType::Case) || self.check(TokenType::Top))
29453            && self.check_next(TokenType::Dot)
29454        {
29455            let token = self.advance();
29456            let ident = Identifier::new(token.text);
29457            self.expect(TokenType::Dot)?;
29458            if self.match_token(TokenType::Star) {
29459                // case.* or top.*
29460                let star = self.parse_star_modifiers(Some(ident))?;
29461                return Ok(Expression::Star(star));
29462            }
29463            // case.column or top.column
29464            let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
29465            // Capture trailing comments from the column name token
29466            let trailing_comments = self.previous_trailing_comments().to_vec();
29467            let mut col = Expression::boxed_column(Column {
29468                name: col_ident,
29469                table: Some(ident),
29470                join_mark: false,
29471                trailing_comments,
29472                span: None,
29473                inferred_type: None,
29474            });
29475            // Handle Oracle/Redshift outer join marker (+) after column reference
29476            if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
29477                let saved_pos = self.current;
29478                if self.match_token(TokenType::LParen)
29479                    && self.match_token(TokenType::Plus)
29480                    && self.match_token(TokenType::RParen)
29481                {
29482                    if let Expression::Column(ref mut c) = col {
29483                        c.join_mark = true;
29484                    }
29485                } else {
29486                    self.current = saved_pos;
29487                }
29488            }
29489            return self.maybe_parse_subscript(col);
29490        }
29491
29492        // MySQL BINARY prefix operator: BINARY expr -> CAST(expr AS BINARY)
29493        // Only treat as prefix operator when followed by an expression (not ( which would be BINARY() function,
29494        // and not when it would be a data type like BINARY in column definitions)
29495        if self.check(TokenType::Var)
29496            && self.peek().text.eq_ignore_ascii_case("BINARY")
29497            && !self.check_next(TokenType::LParen)
29498            && !self.check_next(TokenType::Dot)
29499            && !self.check_next(TokenType::RParen)
29500            && !self.check_next(TokenType::Comma)
29501            && !self.is_at_end()
29502        {
29503            // Check if this is actually followed by an expression token (not end of statement)
29504            let next_idx = self.current + 1;
29505            let has_expr = next_idx < self.tokens.len()
29506                && !matches!(
29507                    self.tokens[next_idx].token_type,
29508                    TokenType::Semicolon | TokenType::Eof | TokenType::RParen | TokenType::Comma
29509                );
29510            if has_expr {
29511                self.skip(); // consume BINARY
29512                let expr = self.parse_unary()?;
29513                return Ok(Expression::Cast(Box::new(Cast {
29514                    this: expr,
29515                    to: DataType::Binary { length: None },
29516                    trailing_comments: Vec::new(),
29517                    double_colon_syntax: false,
29518                    format: None,
29519                    default: None,
29520                    inferred_type: None,
29521                })));
29522            }
29523        }
29524
29525        // RLIKE/REGEXP as function call: RLIKE(expr, pattern, flags)
29526        // Normally RLIKE is an operator, but Snowflake allows function syntax
29527        if self.check(TokenType::RLike) && self.check_next(TokenType::LParen) {
29528            let token = self.advance(); // consume RLIKE
29529            self.skip(); // consume LParen
29530            let args = if self.check(TokenType::RParen) {
29531                Vec::new()
29532            } else {
29533                self.parse_function_arguments()?
29534            };
29535            self.expect(TokenType::RParen)?;
29536            let func = Expression::Function(Box::new(Function {
29537                name: token.text.clone(), // Preserve original case; generator handles normalization
29538                args,
29539                distinct: false,
29540                trailing_comments: Vec::new(),
29541                use_bracket_syntax: false,
29542                no_parens: false,
29543                quoted: false,
29544                span: None,
29545                inferred_type: None,
29546            }));
29547            return self.maybe_parse_over(func);
29548        }
29549
29550        // INSERT as function call: INSERT(str, pos, len, newstr)
29551        // Snowflake/MySQL have INSERT as a string function, but INSERT is also a DML keyword.
29552        // When followed by ( in expression context, treat as function call.
29553        if self.check(TokenType::Insert) && self.check_next(TokenType::LParen) {
29554            let token = self.advance(); // consume INSERT
29555            self.skip(); // consume LParen
29556            let args = if self.check(TokenType::RParen) {
29557                Vec::new()
29558            } else {
29559                self.parse_function_arguments()?
29560            };
29561            self.expect(TokenType::RParen)?;
29562            let func = Expression::Function(Box::new(Function {
29563                name: token.text.clone(),
29564                args,
29565                distinct: false,
29566                trailing_comments: Vec::new(),
29567                use_bracket_syntax: false,
29568                no_parens: false,
29569                quoted: false,
29570                span: None,
29571                inferred_type: None,
29572            }));
29573            return self.maybe_parse_over(func);
29574        }
29575
29576        // ClickHouse: MINUS/EXCEPT/INTERSECT/REGEXP as function names (e.g., minus(a, b), REGEXP('^db'))
29577        // MINUS is tokenized as TokenType::Except (Oracle alias), REGEXP as TokenType::RLike
29578        if matches!(
29579            self.config.dialect,
29580            Some(crate::dialects::DialectType::ClickHouse)
29581        ) && (self.check(TokenType::Except)
29582            || self.check(TokenType::Intersect)
29583            || self.check(TokenType::RLike))
29584            && self.check_next(TokenType::LParen)
29585        {
29586            let token = self.advance(); // consume keyword
29587            self.skip(); // consume LParen
29588            let args = if self.check(TokenType::RParen) {
29589                Vec::new()
29590            } else {
29591                self.parse_function_arguments()?
29592            };
29593            self.expect(TokenType::RParen)?;
29594            let func = Expression::Function(Box::new(Function {
29595                name: token.text.clone(),
29596                args,
29597                distinct: false,
29598                trailing_comments: Vec::new(),
29599                use_bracket_syntax: false,
29600                no_parens: false,
29601                quoted: false,
29602                span: None,
29603                inferred_type: None,
29604            }));
29605            return self.maybe_parse_over(func);
29606        }
29607
29608        // Handle CURRENT_DATE/CURRENT_TIMESTAMP/CURRENT_TIME/CURRENT_DATETIME with parentheses
29609        // These have special token types but BigQuery and others use them as function calls with args
29610        if matches!(
29611            self.peek().token_type,
29612            TokenType::CurrentDate
29613                | TokenType::CurrentTimestamp
29614                | TokenType::CurrentTime
29615                | TokenType::CurrentDateTime
29616        ) {
29617            // Snowflake: CURRENT_TIME / CURRENT_TIME(n) -> Localtime (so DuckDB can output LOCALTIME)
29618            if matches!(
29619                self.config.dialect,
29620                Some(crate::dialects::DialectType::Snowflake)
29621            ) && self.peek().token_type == TokenType::CurrentTime
29622            {
29623                self.skip(); // consume CURRENT_TIME
29624                if self.match_token(TokenType::LParen) {
29625                    // CURRENT_TIME(n) - consume args but ignore precision
29626                    if !self.check(TokenType::RParen) {
29627                        let _ = self.parse_function_arguments()?;
29628                    }
29629                    self.expect(TokenType::RParen)?;
29630                }
29631                return self.maybe_parse_subscript(Expression::Localtime(Box::new(
29632                    crate::expressions::Localtime { this: None },
29633                )));
29634            }
29635            if self.check_next(TokenType::LParen) {
29636                // Parse as function call: CURRENT_DATE('UTC'), CURRENT_TIMESTAMP(), etc.
29637                let token = self.advance(); // consume CURRENT_DATE etc.
29638                self.skip(); // consume LParen
29639                let args = if self.check(TokenType::RParen) {
29640                    Vec::new()
29641                } else {
29642                    self.parse_function_arguments()?
29643                };
29644                self.expect(TokenType::RParen)?;
29645                let func = Expression::Function(Box::new(Function {
29646                    name: token.text.clone(),
29647                    args,
29648                    distinct: false,
29649                    trailing_comments: Vec::new(),
29650                    use_bracket_syntax: false,
29651                    no_parens: false,
29652                    quoted: false,
29653                    span: None,
29654                    inferred_type: None,
29655                }));
29656                return self.maybe_parse_subscript(func);
29657            } else {
29658                // No parens - parse as no-paren function
29659                let token = self.advance();
29660                let func = Expression::Function(Box::new(Function {
29661                    name: token.text.clone(),
29662                    args: Vec::new(),
29663                    distinct: false,
29664                    trailing_comments: Vec::new(),
29665                    use_bracket_syntax: false,
29666                    no_parens: true,
29667                    quoted: false,
29668                    span: None,
29669                    inferred_type: None,
29670                }));
29671                return self.maybe_parse_subscript(func);
29672            }
29673        }
29674
29675        // Type keyword followed by string literal -> CAST('value' AS TYPE)
29676        // E.g., NUMERIC '2.25' -> CAST('2.25' AS NUMERIC)
29677        if self.is_identifier_token() && self.check_next(TokenType::String) {
29678            let upper_name = self.peek().text.to_ascii_uppercase();
29679            if matches!(
29680                upper_name.as_str(),
29681                "NUMERIC" | "DECIMAL" | "BIGNUMERIC" | "BIGDECIMAL"
29682            ) {
29683                self.skip(); // consume the type keyword
29684                let str_token = self.advance(); // consume the string literal
29685                let data_type = match upper_name.as_str() {
29686                    "NUMERIC" | "DECIMAL" | "BIGNUMERIC" | "BIGDECIMAL" => {
29687                        crate::expressions::DataType::Decimal {
29688                            precision: None,
29689                            scale: None,
29690                        }
29691                    }
29692                    _ => unreachable!("type keyword already matched in outer if-condition"),
29693                };
29694                return Ok(Expression::Cast(Box::new(crate::expressions::Cast {
29695                    this: Expression::Literal(Box::new(Literal::String(str_token.text))),
29696                    to: data_type,
29697                    trailing_comments: Vec::new(),
29698                    double_colon_syntax: false,
29699                    format: None,
29700                    default: None,
29701                    inferred_type: None,
29702                })));
29703            }
29704        }
29705
29706        // Identifier, Column, or Function
29707        if self.is_identifier_token() {
29708            // Check for no-paren functions like CURRENT_TIMESTAMP, CURRENT_DATE, etc.
29709            // These should be parsed as functions even without parentheses
29710            let upper_name = self.peek().text.to_ascii_uppercase();
29711            if !self.check_next(TokenType::LParen)
29712                && !self.check_next(TokenType::Dot)
29713                && crate::function_registry::is_no_paren_function_name_upper(upper_name.as_str())
29714                && !(matches!(
29715                    self.config.dialect,
29716                    Some(crate::dialects::DialectType::ClickHouse)
29717                ) && upper_name.as_str() == "CURRENT_TIMESTAMP")
29718            {
29719                let token = self.advance();
29720                let func = Expression::Function(Box::new(Function {
29721                    name: token.text.clone(), // Preserve original case; generator handles normalization
29722                    args: Vec::new(),
29723                    distinct: false,
29724                    trailing_comments: Vec::new(),
29725                    use_bracket_syntax: false,
29726                    no_parens: true, // These functions were called without parentheses
29727                    quoted: false,
29728                    span: None,
29729                    inferred_type: None,
29730                }));
29731                return self.maybe_parse_subscript(func);
29732            }
29733
29734            let ident = self.expect_identifier_with_quoted()?;
29735            let name = ident.name.clone();
29736            let quoted = ident.quoted;
29737
29738            // Check for function call (skip Teradata FORMAT phrase)
29739            let is_teradata_format_phrase = matches!(
29740                self.config.dialect,
29741                Some(crate::dialects::DialectType::Teradata)
29742            ) && self.check(TokenType::LParen)
29743                && self.check_next(TokenType::Format);
29744            if !is_teradata_format_phrase && self.match_token(TokenType::LParen) {
29745                let upper_name = name.to_ascii_uppercase();
29746                let func_expr = self.parse_typed_function(&name, &upper_name, quoted)?;
29747                let func_expr = self.maybe_parse_clickhouse_parameterized_agg(func_expr)?;
29748                // Check for OVER clause (window function)
29749                return self.maybe_parse_over(func_expr);
29750            }
29751
29752            // Check for qualified name (table.column or table.method())
29753            if self.match_token(TokenType::Dot) {
29754                if self.match_token(TokenType::Star) {
29755                    // table.* with potential modifiers
29756                    let star = self.parse_star_modifiers(Some(ident))?;
29757                    let mut star_expr = Expression::Star(star);
29758                    // ClickHouse: a.* APPLY(func) EXCEPT(col) REPLACE(expr AS col) in any order
29759                    if matches!(
29760                        self.config.dialect,
29761                        Some(crate::dialects::DialectType::ClickHouse)
29762                    ) {
29763                        loop {
29764                            if self.check(TokenType::Apply) {
29765                                self.skip();
29766                                let apply_expr = if self.match_token(TokenType::LParen) {
29767                                    let e = self.parse_expression()?;
29768                                    self.expect(TokenType::RParen)?;
29769                                    e
29770                                } else {
29771                                    self.parse_expression()?
29772                                };
29773                                star_expr =
29774                                    Expression::Apply(Box::new(crate::expressions::Apply {
29775                                        this: Box::new(star_expr),
29776                                        expression: Box::new(apply_expr),
29777                                    }));
29778                            } else if self.check(TokenType::Except)
29779                                || self.check(TokenType::Exclude)
29780                            {
29781                                self.skip();
29782                                self.match_identifier("STRICT");
29783                                if self.match_token(TokenType::LParen) {
29784                                    loop {
29785                                        if self.check(TokenType::RParen) {
29786                                            break;
29787                                        }
29788                                        let _ = self.parse_expression()?;
29789                                        if !self.match_token(TokenType::Comma) {
29790                                            break;
29791                                        }
29792                                    }
29793                                    self.expect(TokenType::RParen)?;
29794                                } else if self.is_identifier_token()
29795                                    || self.is_safe_keyword_as_identifier()
29796                                {
29797                                    let _ = self.parse_expression()?;
29798                                }
29799                            } else if self.check(TokenType::Replace) {
29800                                self.skip();
29801                                self.match_identifier("STRICT");
29802                                if self.match_token(TokenType::LParen) {
29803                                    loop {
29804                                        if self.check(TokenType::RParen) {
29805                                            break;
29806                                        }
29807                                        let _ = self.parse_expression()?;
29808                                        if self.match_token(TokenType::As) {
29809                                            if self.is_identifier_token()
29810                                                || self.is_safe_keyword_as_identifier()
29811                                            {
29812                                                self.skip();
29813                                            }
29814                                        }
29815                                        if !self.match_token(TokenType::Comma) {
29816                                            break;
29817                                        }
29818                                    }
29819                                    self.expect(TokenType::RParen)?;
29820                                } else {
29821                                    let _ = self.parse_expression()?;
29822                                    if self.match_token(TokenType::As) {
29823                                        if self.is_identifier_token()
29824                                            || self.is_safe_keyword_as_identifier()
29825                                        {
29826                                            self.skip();
29827                                        }
29828                                    }
29829                                }
29830                            } else {
29831                                break;
29832                            }
29833                        }
29834                    }
29835                    return Ok(star_expr);
29836                }
29837                // Handle numeric field access: a.1, t.2 (ClickHouse tuple field access)
29838                // Also handle negative: a.-1 (ClickHouse negative tuple index)
29839                if self.check(TokenType::Number) {
29840                    let field_name = self.advance().text;
29841                    let col_expr = Expression::Dot(Box::new(DotAccess {
29842                        this: Expression::boxed_column(Column {
29843                            name: ident,
29844                            table: None,
29845                            join_mark: false,
29846                            trailing_comments: Vec::new(),
29847                            span: None,
29848                            inferred_type: None,
29849                        }),
29850                        field: Identifier::new(field_name),
29851                    }));
29852                    return self.maybe_parse_subscript(col_expr);
29853                }
29854                if matches!(
29855                    self.config.dialect,
29856                    Some(crate::dialects::DialectType::ClickHouse)
29857                ) && self.check(TokenType::Dash)
29858                    && self.current + 1 < self.tokens.len()
29859                    && self.tokens[self.current + 1].token_type == TokenType::Number
29860                {
29861                    self.skip(); // consume -
29862                    let num = self.advance().text;
29863                    let field_name = format!("-{}", num);
29864                    let col_expr = Expression::Dot(Box::new(DotAccess {
29865                        this: Expression::boxed_column(Column {
29866                            name: ident,
29867                            table: None,
29868                            join_mark: false,
29869                            trailing_comments: Vec::new(),
29870                            span: None,
29871                            inferred_type: None,
29872                        }),
29873                        field: Identifier::new(field_name),
29874                    }));
29875                    return self.maybe_parse_subscript(col_expr);
29876                }
29877                // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
29878                if matches!(
29879                    self.config.dialect,
29880                    Some(crate::dialects::DialectType::ClickHouse)
29881                ) && self.check(TokenType::Caret)
29882                {
29883                    self.skip(); // consume ^
29884                    let mut field_name = "^".to_string();
29885                    if self.check(TokenType::Identifier)
29886                        || self.check(TokenType::Var)
29887                        || self.check_keyword()
29888                    {
29889                        field_name.push_str(&self.advance().text);
29890                    }
29891                    let col_expr = Expression::Dot(Box::new(DotAccess {
29892                        this: Expression::boxed_column(Column {
29893                            name: ident,
29894                            table: None,
29895                            join_mark: false,
29896                            trailing_comments: Vec::new(),
29897                            span: None,
29898                            inferred_type: None,
29899                        }),
29900                        field: Identifier::new(field_name),
29901                    }));
29902                    return self.maybe_parse_subscript(col_expr);
29903                }
29904                // Allow keywords as column names (e.g., a.filter, x.update)
29905                let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
29906
29907                // Handle Oracle/Redshift outer join marker (+) BEFORE checking for method call
29908                // This is critical: (+) looks like a method call but is actually a join marker
29909                if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
29910                    let saved_pos = self.current;
29911                    if self.match_token(TokenType::LParen)
29912                        && self.match_token(TokenType::Plus)
29913                        && self.match_token(TokenType::RParen)
29914                    {
29915                        let trailing_comments = self.previous_trailing_comments().to_vec();
29916                        let col = Expression::boxed_column(Column {
29917                            name: col_ident,
29918                            table: Some(ident),
29919                            join_mark: true,
29920                            trailing_comments,
29921                            span: None,
29922                            inferred_type: None,
29923                        });
29924                        return self.maybe_parse_subscript(col);
29925                    } else {
29926                        self.current = saved_pos;
29927                    }
29928                }
29929
29930                // Check if this is a method call (column followed by parentheses)
29931                if self.check(TokenType::LParen) {
29932                    // This is a method call like table.EXTRACT() or obj.INT()
29933                    self.skip(); // consume (
29934                    let args = if self.check(TokenType::RParen) {
29935                        Vec::new()
29936                    } else {
29937                        self.parse_expression_list()?
29938                    };
29939                    self.expect(TokenType::RParen)?;
29940                    let method_call = Expression::MethodCall(Box::new(MethodCall {
29941                        this: Expression::boxed_column(Column {
29942                            name: ident.clone(),
29943                            table: None,
29944                            join_mark: false,
29945                            trailing_comments: Vec::new(),
29946                            span: None,
29947                            inferred_type: None,
29948                        }),
29949                        method: col_ident,
29950                        args,
29951                    }));
29952                    return self.maybe_parse_subscript(method_call);
29953                }
29954
29955                // Capture trailing comments from the column name token
29956                let trailing_comments = self.previous_trailing_comments().to_vec();
29957                let col = Expression::boxed_column(Column {
29958                    name: col_ident,
29959                    table: Some(ident),
29960                    join_mark: false,
29961                    trailing_comments,
29962                    span: None,
29963                    inferred_type: None,
29964                });
29965                return self.maybe_parse_subscript(col);
29966            }
29967
29968            // Check for Oracle pseudocolumns (ROWNUM, ROWID, LEVEL, SYSDATE, etc.)
29969            // Oracle pseudocolumns (LEVEL, ROWNUM, ROWID, SYSDATE, etc.)
29970            // Only recognize in Oracle and generic dialect — other dialects treat these as regular identifiers
29971            if !quoted
29972                && matches!(
29973                    self.config.dialect,
29974                    Some(crate::dialects::DialectType::Oracle) | None
29975                )
29976            {
29977                if let Some(pseudocolumn_type) = PseudocolumnType::from_str(&name) {
29978                    return Ok(Expression::Pseudocolumn(Pseudocolumn {
29979                        kind: pseudocolumn_type,
29980                    }));
29981                }
29982            }
29983
29984            // Check for lambda expression: x -> body
29985            // But NOT if followed by a string literal (that's JSON extract: col -> '$.path')
29986            if self.check(TokenType::Arrow)
29987                && !self
29988                    .peek_nth(1)
29989                    .map_or(false, |t| t.token_type == TokenType::String)
29990            {
29991                self.skip(); // consume the Arrow token
29992                let body = self.parse_expression()?;
29993                return Ok(Expression::Lambda(Box::new(LambdaExpr {
29994                    parameters: vec![ident],
29995                    body,
29996                    colon: false,
29997                    parameter_types: Vec::new(),
29998                })));
29999            }
30000
30001            // Capture trailing comments from the identifier token
30002            let trailing_comments = self.previous_trailing_comments().to_vec();
30003            let col = Expression::boxed_column(Column {
30004                name: ident,
30005                table: None,
30006                join_mark: false,
30007                trailing_comments,
30008                span: None,
30009                inferred_type: None,
30010            });
30011            return self.maybe_parse_subscript(col);
30012        }
30013
30014        // Exasol-style IF expression: IF condition THEN true_value ELSE false_value ENDIF
30015        // Check for IF not followed by ( (which would be IF function call handled elsewhere)
30016        // This handles: IF age < 18 THEN 'minor' ELSE 'adult' ENDIF
30017        // IMPORTANT: This must be checked BEFORE is_safe_keyword_as_identifier() which would
30018        // treat IF as a column name when not followed by ( or .
30019        // For TSQL/Fabric: IF (cond) BEGIN ... END is an IF statement, not function
30020        if self.check(TokenType::If)
30021            && !self.check_next(TokenType::Dot)
30022            && (!self.check_next(TokenType::LParen)
30023                || matches!(
30024                    self.config.dialect,
30025                    Some(crate::dialects::DialectType::TSQL)
30026                        | Some(crate::dialects::DialectType::Fabric)
30027                ))
30028        {
30029            let saved_pos = self.current;
30030            self.skip(); // consume IF
30031            if let Some(if_expr) = self.parse_if()? {
30032                return Ok(if_expr);
30033            }
30034            // parse_if() returned None — IF is not an IF expression here,
30035            // restore position so it can be treated as an identifier
30036            self.current = saved_pos;
30037        }
30038
30039        // NEXT VALUE FOR sequence_name [OVER (ORDER BY ...)]
30040        // Must check before treating NEXT as a standalone identifier via is_safe_keyword_as_identifier
30041        if self.check(TokenType::Next)
30042            && self.current + 2 < self.tokens.len()
30043            && self.tokens[self.current + 1]
30044                .text
30045                .eq_ignore_ascii_case("VALUE")
30046            && self.tokens[self.current + 2]
30047                .text
30048                .eq_ignore_ascii_case("FOR")
30049        {
30050            self.skip(); // consume NEXT
30051            if let Some(expr) = self.parse_next_value_for()? {
30052                return Ok(expr);
30053            }
30054        }
30055
30056        // ClickHouse: `from` can be a column name when followed by comma or dot
30057        if matches!(
30058            self.config.dialect,
30059            Some(crate::dialects::DialectType::ClickHouse)
30060        ) && self.check(TokenType::From)
30061            && (self.check_next(TokenType::Comma) || self.check_next(TokenType::Dot))
30062        {
30063            let token = self.advance();
30064            let name = token.text.clone();
30065            if self.match_token(TokenType::Dot) {
30066                // from.col qualified reference
30067                let col_name = self.expect_identifier_or_keyword()?;
30068                return Ok(Expression::Column(Box::new(crate::expressions::Column {
30069                    name: Identifier::new(col_name),
30070                    table: Some(Identifier::new(name)),
30071                    join_mark: false,
30072                    trailing_comments: Vec::new(),
30073                    span: None,
30074                    inferred_type: None,
30075                })));
30076            }
30077            return Ok(Expression::Column(Box::new(crate::expressions::Column {
30078                name: Identifier::new(name),
30079                table: None,
30080                join_mark: false,
30081                trailing_comments: Vec::new(),
30082                span: None,
30083                inferred_type: None,
30084            })));
30085        }
30086
30087        // ClickHouse: `except` as identifier in expression context (set operations are handled at statement level)
30088        // except(args) is already handled above in the MINUS/EXCEPT/INTERSECT function block
30089        if matches!(
30090            self.config.dialect,
30091            Some(crate::dialects::DialectType::ClickHouse)
30092        ) && self.check(TokenType::Except)
30093            && !self.check_next(TokenType::LParen)
30094        {
30095            let token = self.advance();
30096            let name = token.text.clone();
30097            if self.match_token(TokenType::Dot) {
30098                let col_name = self.expect_identifier_or_keyword()?;
30099                return Ok(Expression::Column(Box::new(crate::expressions::Column {
30100                    name: Identifier::new(col_name),
30101                    table: Some(Identifier::new(name)),
30102                    join_mark: false,
30103                    trailing_comments: Vec::new(),
30104                    span: None,
30105                    inferred_type: None,
30106                })));
30107            }
30108            return Ok(Expression::Column(Box::new(crate::expressions::Column {
30109                name: Identifier::new(name),
30110                table: None,
30111                join_mark: false,
30112                trailing_comments: Vec::new(),
30113                span: None,
30114                inferred_type: None,
30115            })));
30116        }
30117
30118        // ClickHouse: structural keywords like FROM, ON, JOIN can be used as identifiers
30119        // in expression context when followed by an operator (e.g., from + 1, on.col)
30120        if matches!(
30121            self.config.dialect,
30122            Some(crate::dialects::DialectType::ClickHouse)
30123        ) && self.peek().token_type.is_keyword()
30124            && !self.is_safe_keyword_as_identifier()
30125        {
30126            let next_tt = self
30127                .peek_nth(1)
30128                .map(|t| t.token_type)
30129                .unwrap_or(TokenType::Semicolon);
30130            // A structural keyword can be used as an identifier when it appears
30131            // in expression context. We detect this by checking what follows.
30132            // Essentially: it's NOT an identifier only if the keyword itself starts
30133            // a clause (e.g., FROM followed by a table name). But when it's followed
30134            // by an operator, comma, close-paren, or even another clause keyword
30135            // (meaning it's the last token in an expression), it's an identifier.
30136            let is_expr_context = !matches!(
30137                next_tt,
30138                TokenType::Identifier
30139                    | TokenType::Var
30140                    | TokenType::QuotedIdentifier
30141                    | TokenType::LParen
30142                    | TokenType::Number
30143                    | TokenType::String
30144            );
30145            if is_expr_context {
30146                let token = self.advance();
30147                return Ok(Expression::boxed_column(Column {
30148                    name: Identifier::new(token.text),
30149                    table: None,
30150                    join_mark: false,
30151                    trailing_comments: Vec::new(),
30152                    span: None,
30153                    inferred_type: None,
30154                }));
30155            }
30156        }
30157        // %s or %(name)s percent parameter (PostgreSQL psycopg2 style)
30158        // Must be checked BEFORE the keyword-as-identifier handler below, since
30159        // Percent is in is_keyword() and is_safe_keyword_as_identifier() returns true for it.
30160        if self.check(TokenType::Percent)
30161            && (
30162                self.check_next(TokenType::Var)  // %s
30163            || self.check_next(TokenType::LParen)
30164                // %(name)s
30165            )
30166        {
30167            self.skip(); // consume %
30168                         // Check for %(name)s - named parameter
30169            if self.match_token(TokenType::LParen) {
30170                // Get the parameter name
30171                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30172                    let name = self.advance().text;
30173                    self.expect(TokenType::RParen)?;
30174                    // Expect 's' after the closing paren
30175                    if self.check(TokenType::Var) && self.peek().text == "s" {
30176                        self.skip(); // consume 's'
30177                    }
30178                    return Ok(Expression::Parameter(Box::new(Parameter {
30179                        name: Some(name),
30180                        index: None,
30181                        style: ParameterStyle::Percent,
30182                        quoted: false,
30183                        string_quoted: false,
30184                        expression: None,
30185                    })));
30186                } else {
30187                    return Err(self.parse_error("Expected parameter name after %("));
30188                }
30189            }
30190            // Check for %s - anonymous parameter
30191            if self.check(TokenType::Var) && self.peek().text == "s" {
30192                self.skip(); // consume 's'
30193                return Ok(Expression::Parameter(Box::new(Parameter {
30194                    name: None,
30195                    index: None,
30196                    style: ParameterStyle::Percent,
30197                    quoted: false,
30198                    string_quoted: false,
30199                    expression: None,
30200                })));
30201            }
30202            // Not a parameter - backtrack
30203            self.current -= 1;
30204        }
30205
30206        // Some keywords can be used as identifiers (column names, table names, etc.)
30207        // when they are "safe" keywords that don't affect query structure.
30208        // Structural keywords like FROM, WHERE, JOIN should NOT be usable as identifiers.
30209        if self.is_safe_keyword_as_identifier() {
30210            let token = self.advance();
30211            let name = token.text.clone();
30212
30213            // Check for function call (keyword followed by paren) - skip Teradata FORMAT phrase
30214            let is_teradata_format_phrase = matches!(
30215                self.config.dialect,
30216                Some(crate::dialects::DialectType::Teradata)
30217            ) && self.check(TokenType::LParen)
30218                && self.check_next(TokenType::Format);
30219            if !is_teradata_format_phrase && self.match_token(TokenType::LParen) {
30220                let upper_name = name.to_ascii_uppercase();
30221                let func_expr = self.parse_typed_function(&name, &upper_name, false)?;
30222                let func_expr = self.maybe_parse_clickhouse_parameterized_agg(func_expr)?;
30223                return self.maybe_parse_over(func_expr);
30224            }
30225
30226            // Check for qualified name (keyword.column or keyword.method())
30227            if self.match_token(TokenType::Dot) {
30228                if self.match_token(TokenType::Star) {
30229                    // keyword.* with potential modifiers
30230                    let ident = Identifier::new(name);
30231                    let star = self.parse_star_modifiers(Some(ident))?;
30232                    return Ok(Expression::Star(star));
30233                }
30234                // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
30235                if matches!(
30236                    self.config.dialect,
30237                    Some(crate::dialects::DialectType::ClickHouse)
30238                ) && self.check(TokenType::Caret)
30239                {
30240                    self.skip(); // consume ^
30241                    let mut field_name = "^".to_string();
30242                    if self.check(TokenType::Identifier)
30243                        || self.check(TokenType::Var)
30244                        || self.check_keyword()
30245                    {
30246                        field_name.push_str(&self.advance().text);
30247                    }
30248                    let col = Expression::Dot(Box::new(DotAccess {
30249                        this: Expression::boxed_column(Column {
30250                            name: Identifier::new(name),
30251                            table: None,
30252                            join_mark: false,
30253                            trailing_comments: Vec::new(),
30254                            span: None,
30255                            inferred_type: None,
30256                        }),
30257                        field: Identifier::new(field_name),
30258                    }));
30259                    return self.maybe_parse_subscript(col);
30260                }
30261
30262                // Handle numeric field access: keyword.1, keyword.2 (ClickHouse tuple field access)
30263                if self.check(TokenType::Number) {
30264                    let field_name = self.advance().text;
30265                    let col_expr = Expression::Dot(Box::new(DotAccess {
30266                        this: Expression::boxed_column(Column {
30267                            name: Identifier::new(name),
30268                            table: None,
30269                            join_mark: false,
30270                            trailing_comments: Vec::new(),
30271                            span: None,
30272                            inferred_type: None,
30273                        }),
30274                        field: Identifier::new(field_name),
30275                    }));
30276                    return self.maybe_parse_subscript(col_expr);
30277                }
30278
30279                // Allow keywords as column names
30280                let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
30281
30282                // Check if this is a method call
30283                if self.check(TokenType::LParen) {
30284                    self.skip(); // consume (
30285                    let args = if self.check(TokenType::RParen) {
30286                        Vec::new()
30287                    } else {
30288                        self.parse_expression_list()?
30289                    };
30290                    self.expect(TokenType::RParen)?;
30291                    let method_call = Expression::MethodCall(Box::new(MethodCall {
30292                        this: Expression::Identifier(Identifier::new(name)),
30293                        method: col_ident,
30294                        args,
30295                    }));
30296                    return self.maybe_parse_subscript(method_call);
30297                }
30298
30299                // Capture trailing comments from the column name token
30300                let trailing_comments = self.previous_trailing_comments().to_vec();
30301                let mut col = Expression::boxed_column(Column {
30302                    name: col_ident,
30303                    table: Some(Identifier::new(name)),
30304                    join_mark: false,
30305                    trailing_comments,
30306                    span: None,
30307                    inferred_type: None,
30308                });
30309                // Handle Oracle/Redshift outer join marker (+) after column reference
30310                if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
30311                    let saved_pos = self.current;
30312                    if self.match_token(TokenType::LParen)
30313                        && self.match_token(TokenType::Plus)
30314                        && self.match_token(TokenType::RParen)
30315                    {
30316                        if let Expression::Column(ref mut c) = col {
30317                            c.join_mark = true;
30318                        }
30319                    } else {
30320                        self.current = saved_pos;
30321                    }
30322                }
30323                return self.maybe_parse_subscript(col);
30324            }
30325
30326            // Simple identifier (keyword used as column name)
30327            // Capture trailing comments from the keyword token
30328            let trailing_comments = self.previous_trailing_comments().to_vec();
30329            let ident = Identifier::new(name);
30330            let col = Expression::boxed_column(Column {
30331                name: ident,
30332                table: None,
30333                join_mark: false,
30334                trailing_comments,
30335                span: None,
30336                inferred_type: None,
30337            });
30338            return self.maybe_parse_subscript(col);
30339        }
30340
30341        // @@ system variable (MySQL/SQL Server): @@version, @@IDENTITY, @@GLOBAL.var
30342        if self.match_token(TokenType::AtAt) {
30343            // Get the variable name
30344            let name = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
30345                let mut n = self.advance().text;
30346                // Handle @@scope.variable (e.g., @@GLOBAL.max_connections, @@SESSION.sql_mode)
30347                if self.match_token(TokenType::Dot) {
30348                    if self.check(TokenType::Identifier)
30349                        || self.check(TokenType::Var)
30350                        || self.is_safe_keyword_as_identifier()
30351                    {
30352                        n.push('.');
30353                        n.push_str(&self.advance().text);
30354                    }
30355                }
30356                n
30357            } else if self.check_keyword() {
30358                // Handle @@keyword (e.g., @@sql_mode when sql_mode is a keyword)
30359                self.advance().text
30360            } else {
30361                return Err(self.parse_error("Expected variable name after @@"));
30362            };
30363            return Ok(Expression::Parameter(Box::new(Parameter {
30364                name: Some(name),
30365                index: None,
30366                style: ParameterStyle::DoubleAt,
30367                quoted: false,
30368                string_quoted: false,
30369                expression: None,
30370            })));
30371        }
30372
30373        // @ user variable/parameter: @x, @"x", @JOIN, @'foo'
30374        if self.match_token(TokenType::DAt) {
30375            // Get the variable name - can be identifier, quoted identifier, keyword, or string
30376            let (name, quoted, string_quoted) =
30377                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
30378                    (self.advance().text, false, false)
30379                } else if self.check(TokenType::QuotedIdentifier) {
30380                    // Quoted identifier like @"x"
30381                    let token = self.advance();
30382                    (token.text, true, false)
30383                } else if self.check(TokenType::String) {
30384                    // String-quoted like @'foo'
30385                    let token = self.advance();
30386                    (token.text, false, true)
30387                } else if self.check(TokenType::Number) {
30388                    // Numeric like @1
30389                    let token = self.advance();
30390                    (token.text, false, false)
30391                } else if self.peek().token_type.is_keyword() {
30392                    // Keyword used as variable name like @JOIN
30393                    let token = self.advance();
30394                    (token.text, false, false)
30395                } else {
30396                    return Err(self.parse_error("Expected variable name after @"));
30397                };
30398            return Ok(Expression::Parameter(Box::new(Parameter {
30399                name: Some(name),
30400                index: None,
30401                style: ParameterStyle::At,
30402                quoted,
30403                string_quoted,
30404                expression: None,
30405            })));
30406        }
30407
30408        // Parameter: ? placeholder or $n positional parameter
30409        if self.check(TokenType::Parameter) {
30410            let token = self.advance();
30411            // Check if this is a positional parameter ($1, $2, etc.) or a plain ? placeholder
30412            if let Ok(index) = token.text.parse::<u32>() {
30413                // Positional parameter like $1, $2 (token text is just the number)
30414                let param = Expression::Parameter(Box::new(Parameter {
30415                    name: None,
30416                    index: Some(index),
30417                    style: ParameterStyle::Dollar,
30418                    quoted: false,
30419                    string_quoted: false,
30420                    expression: None,
30421                }));
30422                // Check for JSON path access: $1:name or dot access: $1.c1
30423                let result = self.parse_colon_json_path(param)?;
30424                return self.maybe_parse_subscript(result);
30425            } else {
30426                // Plain ? placeholder
30427                return Ok(Expression::Placeholder(Placeholder { index: None }));
30428            }
30429        }
30430
30431        // :name or :1 colon parameter
30432        if self.match_token(TokenType::Colon) {
30433            // Check for numeric parameter :1, :2, etc.
30434            if self.check(TokenType::Number) {
30435                let num_token = self.advance();
30436                if let Ok(index) = num_token.text.parse::<u32>() {
30437                    return Ok(Expression::Parameter(Box::new(Parameter {
30438                        name: None,
30439                        index: Some(index),
30440                        style: ParameterStyle::Colon,
30441                        quoted: false,
30442                        string_quoted: false,
30443                        expression: None,
30444                    })));
30445                }
30446                return Err(
30447                    self.parse_error(format!("Invalid colon parameter: :{}", num_token.text))
30448                );
30449            }
30450            // Get the parameter name
30451            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30452                let name = self.advance().text;
30453                return Ok(Expression::Parameter(Box::new(Parameter {
30454                    name: Some(name),
30455                    index: None,
30456                    style: ParameterStyle::Colon,
30457                    quoted: false,
30458                    string_quoted: false,
30459                    expression: None,
30460                })));
30461            } else {
30462                return Err(self.parse_error("Expected parameter name after :"));
30463            }
30464        }
30465
30466        // $n dollar parameter: $1, $2, etc.
30467        if self.match_token(TokenType::Dollar) {
30468            // Check for ${identifier} or ${kind:name} template variable syntax (Databricks, Hive)
30469            // Hive supports ${hiveconf:variable_name} syntax
30470            if self.match_token(TokenType::LBrace) {
30471                // Parse the variable name - can be identifier or keyword
30472                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30473                    let name_token = self.advance();
30474                    // Check for ${kind:name} syntax (e.g., ${hiveconf:some_var})
30475                    let expression = if self.match_token(TokenType::Colon) {
30476                        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30477                            let expr_token = self.advance();
30478                            Some(expr_token.text.clone())
30479                        } else {
30480                            return Err(self.parse_error("Expected identifier after : in ${...}"));
30481                        }
30482                    } else {
30483                        None
30484                    };
30485                    self.expect(TokenType::RBrace)?;
30486                    return Ok(Expression::Parameter(Box::new(Parameter {
30487                        name: Some(name_token.text.clone()),
30488                        index: None,
30489                        style: ParameterStyle::DollarBrace,
30490                        quoted: false,
30491                        string_quoted: false,
30492                        expression,
30493                    })));
30494                } else {
30495                    return Err(self.parse_error("Expected identifier after ${"));
30496                }
30497            }
30498            // Check for number following the dollar sign → positional parameter ($1, $2, etc.)
30499            if self.check(TokenType::Number) {
30500                let num_token = self.advance();
30501                // Parse the number as an index
30502                if let Ok(index) = num_token.text.parse::<u32>() {
30503                    let param_expr = Expression::Parameter(Box::new(Parameter {
30504                        name: None,
30505                        index: Some(index),
30506                        style: ParameterStyle::Dollar,
30507                        quoted: false,
30508                        string_quoted: false,
30509                        expression: None,
30510                    }));
30511                    // Check for JSON path access: $1:name or $1:name:subname
30512                    let result = self.parse_colon_json_path(param_expr)?;
30513                    // Also check for dot access: $1.c1 or $1:name.field
30514                    return self.maybe_parse_subscript(result);
30515                }
30516                // If it's not a valid integer, treat as error
30517                return Err(
30518                    self.parse_error(format!("Invalid dollar parameter: ${}", num_token.text))
30519                );
30520            }
30521            // Check for identifier following the dollar sign → session variable ($x, $query_id, etc.)
30522            if self.check(TokenType::Identifier)
30523                || self.check(TokenType::Var)
30524                || self.is_safe_keyword_as_identifier()
30525            {
30526                let name_token = self.advance();
30527                return Ok(Expression::Parameter(Box::new(Parameter {
30528                    name: Some(name_token.text.clone()),
30529                    index: None,
30530                    style: ParameterStyle::Dollar,
30531                    quoted: false,
30532                    string_quoted: false,
30533                    expression: None,
30534                })));
30535            }
30536            // Just a $ by itself - treat as error
30537            return Err(self.parse_error("Expected number or identifier after $"));
30538        }
30539
30540        // %s or %(name)s percent parameter (PostgreSQL psycopg2 style)
30541        if self.match_token(TokenType::Percent) {
30542            // Check for %(name)s - named parameter
30543            if self.match_token(TokenType::LParen) {
30544                // Get the parameter name
30545                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30546                    let name = self.advance().text;
30547                    self.expect(TokenType::RParen)?;
30548                    // Expect 's' after the closing paren
30549                    if self.check(TokenType::Var) && self.peek().text == "s" {
30550                        self.skip(); // consume 's'
30551                    }
30552                    return Ok(Expression::Parameter(Box::new(Parameter {
30553                        name: Some(name),
30554                        index: None,
30555                        style: ParameterStyle::Percent,
30556                        quoted: false,
30557                        string_quoted: false,
30558                        expression: None,
30559                    })));
30560                } else {
30561                    return Err(self.parse_error("Expected parameter name after %("));
30562                }
30563            }
30564            // Check for %s - anonymous parameter
30565            if self.check(TokenType::Var) && self.peek().text == "s" {
30566                self.skip(); // consume 's'
30567                return Ok(Expression::Parameter(Box::new(Parameter {
30568                    name: None,
30569                    index: None,
30570                    style: ParameterStyle::Percent,
30571                    quoted: false,
30572                    string_quoted: false,
30573                    expression: None,
30574                })));
30575            }
30576            // If not followed by 's' or '(', it's not a parameter - error
30577            return Err(self.parse_error("Expected 's' or '(' after % for parameter"));
30578        }
30579
30580        // LEFT, RIGHT, OUTER, FULL, ALL etc. keywords as identifiers when followed by DOT
30581        // e.g., SELECT LEFT.FOO FROM ... or SELECT all.count FROM ...
30582        if (self.check(TokenType::Left)
30583            || self.check(TokenType::Right)
30584            || self.check(TokenType::Outer)
30585            || self.check(TokenType::Full)
30586            || self.check(TokenType::All)
30587            || self.check(TokenType::Only)
30588            || self.check(TokenType::Next)
30589            || self.check(TokenType::If))
30590            && self.check_next(TokenType::Dot)
30591        {
30592            let token = self.advance();
30593            let ident = Identifier::new(token.text);
30594            self.expect(TokenType::Dot)?;
30595            if self.match_token(TokenType::Star) {
30596                let star = self.parse_star_modifiers(Some(ident))?;
30597                return Ok(Expression::Star(star));
30598            }
30599            let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
30600            let trailing_comments = self.previous_trailing_comments().to_vec();
30601            let mut col = Expression::boxed_column(Column {
30602                name: col_ident,
30603                table: Some(ident),
30604                join_mark: false,
30605                trailing_comments,
30606                span: None,
30607                inferred_type: None,
30608            });
30609            // Handle Oracle/Redshift outer join marker (+) after column reference
30610            if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
30611                let saved_pos = self.current;
30612                if self.match_token(TokenType::LParen)
30613                    && self.match_token(TokenType::Plus)
30614                    && self.match_token(TokenType::RParen)
30615                {
30616                    if let Expression::Column(ref mut c) = col {
30617                        c.join_mark = true;
30618                    }
30619                } else {
30620                    self.current = saved_pos;
30621                }
30622            }
30623            return self.maybe_parse_subscript(col);
30624        }
30625
30626        // NEXT VALUE FOR sequence_name [OVER (ORDER BY ...)]
30627        // Must check before treating NEXT as a standalone identifier
30628        if self.check(TokenType::Next) {
30629            // NEXT(arg) - pattern navigation function in MATCH_RECOGNIZE
30630            if self.check_next(TokenType::LParen) {
30631                let token = self.advance();
30632                self.skip(); // consume LParen
30633                let args = self.parse_function_args_list()?;
30634                self.expect(TokenType::RParen)?;
30635                return Ok(Expression::Function(Box::new(Function {
30636                    name: token.text,
30637                    args,
30638                    distinct: false,
30639                    trailing_comments: Vec::new(),
30640                    use_bracket_syntax: false,
30641                    no_parens: false,
30642                    quoted: false,
30643                    span: None,
30644                    inferred_type: None,
30645                })));
30646            }
30647        }
30648
30649        // LEFT, RIGHT, OUTER, FULL, ONLY, NEXT as standalone identifiers (not followed by JOIN or LParen)
30650        // e.g., SELECT LEFT FROM ... or SELECT only FROM ...
30651        // If followed by LParen, it's a function call (e.g., NEXT(bar) in MATCH_RECOGNIZE)
30652        if self.can_be_alias_keyword()
30653            && !self.check_next(TokenType::Join)
30654            && !self.check_next(TokenType::LParen)
30655        {
30656            let token = self.advance();
30657            let trailing_comments = self.previous_trailing_comments().to_vec();
30658            let col = Expression::boxed_column(Column {
30659                name: Identifier::new(token.text),
30660                table: None,
30661                join_mark: false,
30662                trailing_comments,
30663                span: None,
30664                inferred_type: None,
30665            });
30666            return self.maybe_parse_subscript(col);
30667        }
30668
30669        Err(self.parse_error(format!("Unexpected token: {:?}", self.peek().token_type)))
30670    }
30671
30672    /// Check if function name is a known aggregate function
30673    fn is_aggregate_function(name: &str) -> bool {
30674        crate::function_registry::is_aggregate_function_name(name)
30675    }
30676
30677    /// Whether the source dialect uses LOG(base, value) order (base first).
30678    /// Default is true. BigQuery, TSQL, Tableau, Fabric use LOG(value, base).
30679    fn log_base_first(&self) -> bool {
30680        !matches!(
30681            self.config.dialect,
30682            Some(crate::dialects::DialectType::BigQuery)
30683                | Some(crate::dialects::DialectType::TSQL)
30684                | Some(crate::dialects::DialectType::Tableau)
30685                | Some(crate::dialects::DialectType::Fabric)
30686        )
30687    }
30688
30689    /// Whether the source dialect treats single-arg LOG(x) as LN(x).
30690    /// These dialects have LOG_DEFAULTS_TO_LN = True in Python sqlglot.
30691    fn log_defaults_to_ln(&self) -> bool {
30692        matches!(
30693            self.config.dialect,
30694            Some(crate::dialects::DialectType::MySQL)
30695                | Some(crate::dialects::DialectType::BigQuery)
30696                | Some(crate::dialects::DialectType::TSQL)
30697                | Some(crate::dialects::DialectType::ClickHouse)
30698                | Some(crate::dialects::DialectType::Hive)
30699                | Some(crate::dialects::DialectType::Spark)
30700                | Some(crate::dialects::DialectType::Databricks)
30701                | Some(crate::dialects::DialectType::Drill)
30702                | Some(crate::dialects::DialectType::Dremio)
30703        )
30704    }
30705
30706    /// Parse the subset of typed functions that are handled via function-registry metadata.
30707    fn try_parse_registry_typed_function(
30708        &mut self,
30709        name: &str,
30710        upper_name: &str,
30711        canonical_upper_name: &str,
30712        quoted: bool,
30713    ) -> Result<Option<Expression>> {
30714        let Some(spec) =
30715            crate::function_registry::typed_function_spec_by_canonical_upper(canonical_upper_name)
30716        else {
30717            return Ok(None);
30718        };
30719
30720        match (spec.parse_kind, spec.canonical_name) {
30721            (crate::function_registry::TypedParseKind::AggregateLike, "COUNT_IF") => {
30722                let distinct = self.match_token(TokenType::Distinct);
30723                let this = self.parse_expression()?;
30724                // ClickHouse: handle AS alias inside countIf args: countIf(expr AS d, pred)
30725                let this = if matches!(
30726                    self.config.dialect,
30727                    Some(crate::dialects::DialectType::ClickHouse)
30728                ) && self.check(TokenType::As)
30729                {
30730                    let next_idx = self.current + 1;
30731                    let after_alias_idx = self.current + 2;
30732                    let is_alias = next_idx < self.tokens.len()
30733                        && (matches!(
30734                            self.tokens[next_idx].token_type,
30735                            TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
30736                        ) || self.tokens[next_idx].token_type.is_keyword())
30737                        && after_alias_idx < self.tokens.len()
30738                        && matches!(
30739                            self.tokens[after_alias_idx].token_type,
30740                            TokenType::RParen | TokenType::Comma
30741                        );
30742                    if is_alias {
30743                        self.skip(); // consume AS
30744                        let alias_token = self.advance();
30745                        Expression::Alias(Box::new(crate::expressions::Alias {
30746                            this,
30747                            alias: Identifier::new(alias_token.text.clone()),
30748                            column_aliases: Vec::new(),
30749                            pre_alias_comments: Vec::new(),
30750                            trailing_comments: Vec::new(),
30751                            inferred_type: None,
30752                        }))
30753                    } else {
30754                        this
30755                    }
30756                } else {
30757                    this
30758                };
30759                if matches!(
30760                    self.config.dialect,
30761                    Some(crate::dialects::DialectType::ClickHouse)
30762                ) && self.match_token(TokenType::Comma)
30763                {
30764                    let mut args = vec![this];
30765                    let arg = self.parse_expression()?;
30766                    // Handle AS alias on subsequent args too
30767                    let arg = if self.check(TokenType::As) {
30768                        let next_idx = self.current + 1;
30769                        let after_alias_idx = self.current + 2;
30770                        let is_alias = next_idx < self.tokens.len()
30771                            && (matches!(
30772                                self.tokens[next_idx].token_type,
30773                                TokenType::Identifier
30774                                    | TokenType::Var
30775                                    | TokenType::QuotedIdentifier
30776                            ) || self.tokens[next_idx].token_type.is_keyword())
30777                            && after_alias_idx < self.tokens.len()
30778                            && matches!(
30779                                self.tokens[after_alias_idx].token_type,
30780                                TokenType::RParen | TokenType::Comma
30781                            );
30782                        if is_alias {
30783                            self.skip(); // consume AS
30784                            let alias_token = self.advance();
30785                            Expression::Alias(Box::new(crate::expressions::Alias {
30786                                this: arg,
30787                                alias: Identifier::new(alias_token.text.clone()),
30788                                column_aliases: Vec::new(),
30789                                pre_alias_comments: Vec::new(),
30790                                trailing_comments: Vec::new(),
30791                                inferred_type: None,
30792                            }))
30793                        } else {
30794                            arg
30795                        }
30796                    } else {
30797                        arg
30798                    };
30799                    args.push(arg);
30800                    while self.match_token(TokenType::Comma) {
30801                        args.push(self.parse_expression()?);
30802                    }
30803                    self.expect(TokenType::RParen)?;
30804                    return Ok(Some(Expression::CombinedAggFunc(Box::new(
30805                        CombinedAggFunc {
30806                            this: Box::new(Expression::Identifier(Identifier::new("countIf"))),
30807                            expressions: args,
30808                        },
30809                    ))));
30810                }
30811                self.expect(TokenType::RParen)?;
30812                let filter = self.parse_filter_clause()?;
30813                Ok(Some(Expression::CountIf(Box::new(AggFunc {
30814                    ignore_nulls: None,
30815                    this,
30816                    distinct,
30817                    filter,
30818                    order_by: Vec::new(),
30819                    having_max: None,
30820                    name: Some(name.to_string()),
30821                    limit: None,
30822                    inferred_type: None,
30823                }))))
30824            }
30825            (crate::function_registry::TypedParseKind::Binary, "STARTS_WITH")
30826            | (crate::function_registry::TypedParseKind::Binary, "ENDS_WITH") => {
30827                let this = self.parse_expression()?;
30828                self.expect(TokenType::Comma)?;
30829                let expression = self.parse_expression()?;
30830                self.expect(TokenType::RParen)?;
30831                let func = BinaryFunc {
30832                    original_name: None,
30833                    this,
30834                    expression,
30835                    inferred_type: None,
30836                };
30837                let expr = match spec.canonical_name {
30838                    "STARTS_WITH" => Expression::StartsWith(Box::new(func)),
30839                    "ENDS_WITH" => Expression::EndsWith(Box::new(func)),
30840                    _ => unreachable!("binary typed parse kind already matched in caller"),
30841                };
30842                Ok(Some(expr))
30843            }
30844            (crate::function_registry::TypedParseKind::Binary, "ATAN2") => {
30845                let this = self.parse_expression()?;
30846                self.expect(TokenType::Comma)?;
30847                let expression = self.parse_expression()?;
30848                self.expect(TokenType::RParen)?;
30849                Ok(Some(Expression::Atan2(Box::new(BinaryFunc {
30850                    original_name: None,
30851                    this,
30852                    expression,
30853                    inferred_type: None,
30854                }))))
30855            }
30856            (crate::function_registry::TypedParseKind::Binary, "MAP_FROM_ARRAYS")
30857            | (crate::function_registry::TypedParseKind::Binary, "MAP_CONTAINS_KEY")
30858            | (crate::function_registry::TypedParseKind::Binary, "ELEMENT_AT") => {
30859                let this = self.parse_expression()?;
30860                self.expect(TokenType::Comma)?;
30861                let expression = self.parse_expression()?;
30862                self.expect(TokenType::RParen)?;
30863                let func = BinaryFunc {
30864                    original_name: None,
30865                    this,
30866                    expression,
30867                    inferred_type: None,
30868                };
30869                let expr = match spec.canonical_name {
30870                    "MAP_FROM_ARRAYS" => Expression::MapFromArrays(Box::new(func)),
30871                    "MAP_CONTAINS_KEY" => Expression::MapContainsKey(Box::new(func)),
30872                    "ELEMENT_AT" => Expression::ElementAt(Box::new(func)),
30873                    _ => unreachable!("binary map parse kind already matched in caller"),
30874                };
30875                Ok(Some(expr))
30876            }
30877            (crate::function_registry::TypedParseKind::Binary, "CONTAINS")
30878            | (crate::function_registry::TypedParseKind::Binary, "MOD")
30879            | (crate::function_registry::TypedParseKind::Binary, "POW") => {
30880                let this = self.parse_expression()?;
30881                self.expect(TokenType::Comma)?;
30882                let expression = self.parse_expression()?;
30883                self.expect(TokenType::RParen)?;
30884                let expr = match spec.canonical_name {
30885                    "CONTAINS" => Expression::Contains(Box::new(BinaryFunc {
30886                        original_name: None,
30887                        this,
30888                        expression,
30889                        inferred_type: None,
30890                    })),
30891                    "MOD" => Expression::ModFunc(Box::new(BinaryFunc {
30892                        original_name: None,
30893                        this,
30894                        expression,
30895                        inferred_type: None,
30896                    })),
30897                    "POW" => Expression::Power(Box::new(BinaryFunc {
30898                        original_name: None,
30899                        this,
30900                        expression,
30901                        inferred_type: None,
30902                    })),
30903                    _ => unreachable!("binary scalar parse kind already matched in caller"),
30904                };
30905                Ok(Some(expr))
30906            }
30907            (crate::function_registry::TypedParseKind::Binary, "ADD_MONTHS")
30908            | (crate::function_registry::TypedParseKind::Binary, "MONTHS_BETWEEN")
30909            | (crate::function_registry::TypedParseKind::Binary, "NEXT_DAY") => {
30910                let this = self.parse_expression()?;
30911                self.expect(TokenType::Comma)?;
30912                let expression = self.parse_expression()?;
30913                if spec.canonical_name == "MONTHS_BETWEEN" && self.match_token(TokenType::Comma) {
30914                    let round_off = self.parse_expression()?;
30915                    self.expect(TokenType::RParen)?;
30916                    return Ok(Some(Expression::Function(Box::new(
30917                        crate::expressions::Function::new(
30918                            "MONTHS_BETWEEN".to_string(),
30919                            vec![this, expression, round_off],
30920                        ),
30921                    ))));
30922                }
30923                self.expect(TokenType::RParen)?;
30924                let func = BinaryFunc {
30925                    original_name: None,
30926                    this,
30927                    expression,
30928                    inferred_type: None,
30929                };
30930                let expr = match spec.canonical_name {
30931                    "ADD_MONTHS" => Expression::AddMonths(Box::new(func)),
30932                    "MONTHS_BETWEEN" => Expression::MonthsBetween(Box::new(func)),
30933                    "NEXT_DAY" => Expression::NextDay(Box::new(func)),
30934                    _ => unreachable!("date binary parse kind already matched in caller"),
30935                };
30936                Ok(Some(expr))
30937            }
30938            (crate::function_registry::TypedParseKind::Binary, "ARRAY_CONTAINS")
30939            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_POSITION")
30940            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_APPEND")
30941            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_PREPEND")
30942            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_UNION")
30943            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_EXCEPT")
30944            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_REMOVE") => {
30945                let this = self.parse_expression()?;
30946                self.expect(TokenType::Comma)?;
30947                let expression = self.parse_expression()?;
30948                self.expect(TokenType::RParen)?;
30949                let func = BinaryFunc {
30950                    original_name: None,
30951                    this,
30952                    expression,
30953                    inferred_type: None,
30954                };
30955                let expr = match spec.canonical_name {
30956                    "ARRAY_CONTAINS" => Expression::ArrayContains(Box::new(func)),
30957                    "ARRAY_POSITION" => Expression::ArrayPosition(Box::new(func)),
30958                    "ARRAY_APPEND" => Expression::ArrayAppend(Box::new(func)),
30959                    "ARRAY_PREPEND" => Expression::ArrayPrepend(Box::new(func)),
30960                    "ARRAY_UNION" => Expression::ArrayUnion(Box::new(func)),
30961                    "ARRAY_EXCEPT" => Expression::ArrayExcept(Box::new(func)),
30962                    "ARRAY_REMOVE" => Expression::ArrayRemove(Box::new(func)),
30963                    _ => unreachable!("array binary parse kind already matched in caller"),
30964                };
30965                Ok(Some(expr))
30966            }
30967            (crate::function_registry::TypedParseKind::Unary, "LENGTH") => {
30968                let this = self.parse_expression()?;
30969                // PostgreSQL: LENGTH(string, encoding) accepts optional second argument
30970                if self.match_token(TokenType::Comma) {
30971                    let encoding = self.parse_expression()?;
30972                    self.expect(TokenType::RParen)?;
30973                    // Store as a regular function to preserve both arguments
30974                    Ok(Some(Expression::Function(Box::new(Function::new(
30975                        upper_name,
30976                        vec![this, encoding],
30977                    )))))
30978                } else {
30979                    self.expect(TokenType::RParen)?;
30980                    Ok(Some(Expression::Length(Box::new(UnaryFunc::new(this)))))
30981                }
30982            }
30983            (crate::function_registry::TypedParseKind::Unary, "LOWER") => {
30984                let this = self.parse_expression_with_clickhouse_alias()?;
30985                self.expect(TokenType::RParen)?;
30986                Ok(Some(Expression::Lower(Box::new(UnaryFunc::new(this)))))
30987            }
30988            (crate::function_registry::TypedParseKind::Unary, "UPPER") => {
30989                let this = self.parse_expression_with_clickhouse_alias()?;
30990                self.expect(TokenType::RParen)?;
30991                Ok(Some(Expression::Upper(Box::new(UnaryFunc::new(this)))))
30992            }
30993            (crate::function_registry::TypedParseKind::Unary, "TYPEOF") => {
30994                let this = self.parse_expression()?;
30995                // ClickHouse: expr AS alias inside function args
30996                let this = self.maybe_clickhouse_alias(this);
30997                if self.match_token(TokenType::Comma) {
30998                    // Preserve additional args via generic function form
30999                    let mut all_args = vec![this];
31000                    let remaining = self.parse_function_arguments()?;
31001                    all_args.extend(remaining);
31002                    self.expect(TokenType::RParen)?;
31003                    Ok(Some(Expression::Function(Box::new(Function {
31004                        name: name.to_string(),
31005                        args: all_args,
31006                        distinct: false,
31007                        trailing_comments: Vec::new(),
31008                        use_bracket_syntax: false,
31009                        no_parens: false,
31010                        quoted: false,
31011                        span: None,
31012                        inferred_type: None,
31013                    }))))
31014                } else {
31015                    self.expect(TokenType::RParen)?;
31016                    Ok(Some(Expression::Typeof(Box::new(UnaryFunc::new(this)))))
31017                }
31018            }
31019            (crate::function_registry::TypedParseKind::Unary, "DAYOFWEEK")
31020            | (crate::function_registry::TypedParseKind::Unary, "DAYOFYEAR")
31021            | (crate::function_registry::TypedParseKind::Unary, "DAYOFMONTH")
31022            | (crate::function_registry::TypedParseKind::Unary, "WEEKOFYEAR") => {
31023                let this = self.parse_expression()?;
31024                self.expect(TokenType::RParen)?;
31025                let func = UnaryFunc::new(this);
31026                let expr = match spec.canonical_name {
31027                    "DAYOFWEEK" => Expression::DayOfWeek(Box::new(func)),
31028                    "DAYOFYEAR" => Expression::DayOfYear(Box::new(func)),
31029                    "DAYOFMONTH" => Expression::DayOfMonth(Box::new(func)),
31030                    "WEEKOFYEAR" => Expression::WeekOfYear(Box::new(func)),
31031                    _ => unreachable!("date-part unary parse kind already matched in caller"),
31032                };
31033                Ok(Some(expr))
31034            }
31035            (crate::function_registry::TypedParseKind::Unary, "SIN")
31036            | (crate::function_registry::TypedParseKind::Unary, "COS")
31037            | (crate::function_registry::TypedParseKind::Unary, "TAN")
31038            | (crate::function_registry::TypedParseKind::Unary, "ASIN")
31039            | (crate::function_registry::TypedParseKind::Unary, "ACOS")
31040            | (crate::function_registry::TypedParseKind::Unary, "ATAN")
31041            | (crate::function_registry::TypedParseKind::Unary, "RADIANS")
31042            | (crate::function_registry::TypedParseKind::Unary, "DEGREES") => {
31043                let this = self.parse_expression()?;
31044                // MySQL: ATAN(y, x) with 2 args is equivalent to ATAN2(y, x)
31045                if spec.canonical_name == "ATAN" && self.match_token(TokenType::Comma) {
31046                    let expression = self.parse_expression()?;
31047                    self.expect(TokenType::RParen)?;
31048                    return Ok(Some(Expression::Atan2(Box::new(BinaryFunc {
31049                        original_name: Some("ATAN".to_string()),
31050                        this,
31051                        expression,
31052                        inferred_type: None,
31053                    }))));
31054                }
31055                self.expect(TokenType::RParen)?;
31056                let func = UnaryFunc::new(this);
31057                let expr = match spec.canonical_name {
31058                    "SIN" => Expression::Sin(Box::new(func)),
31059                    "COS" => Expression::Cos(Box::new(func)),
31060                    "TAN" => Expression::Tan(Box::new(func)),
31061                    "ASIN" => Expression::Asin(Box::new(func)),
31062                    "ACOS" => Expression::Acos(Box::new(func)),
31063                    "ATAN" => Expression::Atan(Box::new(func)),
31064                    "RADIANS" => Expression::Radians(Box::new(func)),
31065                    "DEGREES" => Expression::Degrees(Box::new(func)),
31066                    _ => unreachable!("trig unary parse kind already matched in caller"),
31067                };
31068                Ok(Some(expr))
31069            }
31070            (crate::function_registry::TypedParseKind::Unary, "YEAR")
31071            | (crate::function_registry::TypedParseKind::Unary, "MONTH")
31072            | (crate::function_registry::TypedParseKind::Unary, "DAY")
31073            | (crate::function_registry::TypedParseKind::Unary, "HOUR")
31074            | (crate::function_registry::TypedParseKind::Unary, "MINUTE")
31075            | (crate::function_registry::TypedParseKind::Unary, "SECOND")
31076            | (crate::function_registry::TypedParseKind::Unary, "DAYOFWEEK_ISO")
31077            | (crate::function_registry::TypedParseKind::Unary, "QUARTER")
31078            | (crate::function_registry::TypedParseKind::Unary, "EPOCH")
31079            | (crate::function_registry::TypedParseKind::Unary, "EPOCH_MS") => {
31080                let this = self.parse_expression()?;
31081                self.expect(TokenType::RParen)?;
31082                let func = UnaryFunc::new(this);
31083                let expr = match spec.canonical_name {
31084                    "YEAR" => Expression::Year(Box::new(func)),
31085                    "MONTH" => Expression::Month(Box::new(func)),
31086                    "DAY" => Expression::Day(Box::new(func)),
31087                    "HOUR" => Expression::Hour(Box::new(func)),
31088                    "MINUTE" => Expression::Minute(Box::new(func)),
31089                    "SECOND" => Expression::Second(Box::new(func)),
31090                    "DAYOFWEEK_ISO" => Expression::DayOfWeekIso(Box::new(func)),
31091                    "QUARTER" => Expression::Quarter(Box::new(func)),
31092                    "EPOCH" => Expression::Epoch(Box::new(func)),
31093                    "EPOCH_MS" => Expression::EpochMs(Box::new(func)),
31094                    _ => unreachable!("date unary parse kind already matched in caller"),
31095                };
31096                Ok(Some(expr))
31097            }
31098            (crate::function_registry::TypedParseKind::Unary, "ARRAY_LENGTH")
31099            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_SIZE")
31100            | (crate::function_registry::TypedParseKind::Unary, "CARDINALITY")
31101            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_REVERSE")
31102            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_DISTINCT")
31103            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_COMPACT")
31104            | (crate::function_registry::TypedParseKind::Unary, "EXPLODE")
31105            | (crate::function_registry::TypedParseKind::Unary, "EXPLODE_OUTER") => {
31106                let this = self.parse_expression()?;
31107                // PostgreSQL ARRAY_LENGTH and ARRAY_SIZE can take a second dimension arg.
31108                // Preserve that by falling back to generic function form for 2-arg usage.
31109                if (spec.canonical_name == "ARRAY_LENGTH" || spec.canonical_name == "ARRAY_SIZE")
31110                    && self.match_token(TokenType::Comma)
31111                {
31112                    let dimension = self.parse_expression()?;
31113                    self.expect(TokenType::RParen)?;
31114                    return Ok(Some(Expression::Function(Box::new(Function {
31115                        name: name.to_string(),
31116                        args: vec![this, dimension],
31117                        distinct: false,
31118                        trailing_comments: Vec::new(),
31119                        use_bracket_syntax: false,
31120                        no_parens: false,
31121                        quoted: false,
31122                        span: None,
31123                        inferred_type: None,
31124                    }))));
31125                }
31126                self.expect(TokenType::RParen)?;
31127                let func = UnaryFunc::new(this);
31128                let expr = match spec.canonical_name {
31129                    "ARRAY_LENGTH" => Expression::ArrayLength(Box::new(func)),
31130                    "ARRAY_SIZE" => Expression::ArraySize(Box::new(func)),
31131                    "CARDINALITY" => Expression::Cardinality(Box::new(func)),
31132                    "ARRAY_REVERSE" => Expression::ArrayReverse(Box::new(func)),
31133                    "ARRAY_DISTINCT" => Expression::ArrayDistinct(Box::new(func)),
31134                    "ARRAY_COMPACT" => Expression::ArrayCompact(Box::new(func)),
31135                    "EXPLODE" => Expression::Explode(Box::new(func)),
31136                    "EXPLODE_OUTER" => Expression::ExplodeOuter(Box::new(func)),
31137                    _ => unreachable!("array unary parse kind already matched in caller"),
31138                };
31139                Ok(Some(expr))
31140            }
31141            (crate::function_registry::TypedParseKind::Unary, "MAP_FROM_ENTRIES")
31142            | (crate::function_registry::TypedParseKind::Unary, "MAP_KEYS")
31143            | (crate::function_registry::TypedParseKind::Unary, "MAP_VALUES") => {
31144                let this = self.parse_expression()?;
31145                self.expect(TokenType::RParen)?;
31146                let func = UnaryFunc::new(this);
31147                let expr = match spec.canonical_name {
31148                    "MAP_FROM_ENTRIES" => Expression::MapFromEntries(Box::new(func)),
31149                    "MAP_KEYS" => Expression::MapKeys(Box::new(func)),
31150                    "MAP_VALUES" => Expression::MapValues(Box::new(func)),
31151                    _ => unreachable!("map unary parse kind already matched in caller"),
31152                };
31153                Ok(Some(expr))
31154            }
31155            (crate::function_registry::TypedParseKind::Unary, "ABS") => {
31156                let this = self.parse_expression_with_clickhouse_alias()?;
31157                self.expect(TokenType::RParen)?;
31158                Ok(Some(Expression::Abs(Box::new(UnaryFunc::new(this)))))
31159            }
31160            (crate::function_registry::TypedParseKind::Unary, "SQRT")
31161            | (crate::function_registry::TypedParseKind::Unary, "EXP")
31162            | (crate::function_registry::TypedParseKind::Unary, "LN") => {
31163                let this = self.parse_expression()?;
31164                self.expect(TokenType::RParen)?;
31165                let expr = match spec.canonical_name {
31166                    "SQRT" => Expression::Sqrt(Box::new(UnaryFunc::new(this))),
31167                    "EXP" => Expression::Exp(Box::new(UnaryFunc::new(this))),
31168                    "LN" => Expression::Ln(Box::new(UnaryFunc::new(this))),
31169                    _ => unreachable!("math unary parse kind already matched in caller"),
31170                };
31171                Ok(Some(expr))
31172            }
31173            (crate::function_registry::TypedParseKind::Variadic, "TO_NUMBER")
31174            | (crate::function_registry::TypedParseKind::Variadic, "TRY_TO_NUMBER") => {
31175                let args = self.parse_expression_list()?;
31176                self.expect(TokenType::RParen)?;
31177                let this = args.get(0).cloned().unwrap_or(Expression::Null(Null {}));
31178                let format = args.get(1).cloned().map(Box::new);
31179                let precision = args.get(2).cloned().map(Box::new);
31180                let scale = args.get(3).cloned().map(Box::new);
31181                let safe = if spec.canonical_name == "TRY_TO_NUMBER" {
31182                    Some(Box::new(Expression::Boolean(BooleanLiteral {
31183                        value: true,
31184                    })))
31185                } else {
31186                    None
31187                };
31188                Ok(Some(Expression::ToNumber(Box::new(ToNumber {
31189                    this: Box::new(this),
31190                    format,
31191                    nlsparam: None,
31192                    precision,
31193                    scale,
31194                    safe,
31195                    safe_name: None,
31196                }))))
31197            }
31198            (crate::function_registry::TypedParseKind::Variadic, "SUBSTRING") => {
31199                let this = self.parse_expression()?;
31200                // ClickHouse: implicit/explicit alias: substring('1234' lhs FROM 2) or substring('1234' AS lhs FROM 2)
31201                let this = self.try_clickhouse_func_arg_alias(this);
31202
31203                // Check for SQL standard FROM syntax: SUBSTRING(str FROM pos [FOR len])
31204                if self.match_token(TokenType::From) {
31205                    let start = self.parse_expression()?;
31206                    let start = self.try_clickhouse_func_arg_alias(start);
31207                    let length = if self.match_token(TokenType::For) {
31208                        let len = self.parse_expression()?;
31209                        Some(self.try_clickhouse_func_arg_alias(len))
31210                    } else {
31211                        None
31212                    };
31213                    self.expect(TokenType::RParen)?;
31214                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
31215                        this,
31216                        start,
31217                        length,
31218                        from_for_syntax: true,
31219                    }))))
31220                } else if self.match_token(TokenType::For) {
31221                    // PostgreSQL: SUBSTRING(str FOR len) or SUBSTRING(str FOR len FROM pos)
31222                    let length_expr = self.parse_expression()?;
31223                    let length_expr = self.try_clickhouse_func_arg_alias(length_expr);
31224                    let start = if self.match_token(TokenType::From) {
31225                        let s = self.parse_expression()?;
31226                        self.try_clickhouse_func_arg_alias(s)
31227                    } else {
31228                        // No FROM, use 1 as default start position
31229                        Expression::Literal(Box::new(Literal::Number("1".to_string())))
31230                    };
31231                    self.expect(TokenType::RParen)?;
31232                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
31233                        this,
31234                        start,
31235                        length: Some(length_expr),
31236                        from_for_syntax: true,
31237                    }))))
31238                } else if self.match_token(TokenType::Comma) {
31239                    // Comma-separated syntax: SUBSTRING(str, pos) or SUBSTRING(str, pos, len)
31240                    let start = self.parse_expression()?;
31241                    let start = self.try_clickhouse_func_arg_alias(start);
31242                    let length = if self.match_token(TokenType::Comma) {
31243                        let len = self.parse_expression()?;
31244                        Some(self.try_clickhouse_func_arg_alias(len))
31245                    } else {
31246                        None
31247                    };
31248                    self.expect(TokenType::RParen)?;
31249                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
31250                        this,
31251                        start,
31252                        length,
31253                        from_for_syntax: false,
31254                    }))))
31255                } else {
31256                    // Just SUBSTRING(str) with no other args - unusual but handle it
31257                    self.expect(TokenType::RParen)?;
31258                    // Treat as function call
31259                    Ok(Some(Expression::Function(Box::new(Function {
31260                        name: name.to_string(),
31261                        args: vec![this],
31262                        distinct: false,
31263                        trailing_comments: Vec::new(),
31264                        use_bracket_syntax: false,
31265                        no_parens: false,
31266                        quoted: false,
31267                        span: None,
31268                        inferred_type: None,
31269                    }))))
31270                }
31271            }
31272            (crate::function_registry::TypedParseKind::Variadic, "DATE_PART") => {
31273                let part = self.parse_expression()?;
31274                // For TSQL/Fabric, normalize date part aliases (e.g., "dd" -> DAY)
31275                let mut part = if matches!(
31276                    self.config.dialect,
31277                    Some(crate::dialects::DialectType::TSQL)
31278                        | Some(crate::dialects::DialectType::Fabric)
31279                ) {
31280                    self.normalize_tsql_date_part(part)
31281                } else {
31282                    part
31283                };
31284                // Accept both FROM and comma as separator (Snowflake supports both syntaxes)
31285                if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
31286                    return Err(self.parse_error("Expected FROM or comma in DATE_PART"));
31287                }
31288                let from_expr = self.parse_expression()?;
31289                self.expect(TokenType::RParen)?;
31290                if matches!(
31291                    self.config.dialect,
31292                    Some(crate::dialects::DialectType::Snowflake)
31293                ) {
31294                    if self
31295                        .try_parse_date_part_field_identifier_expr(&part)
31296                        .is_some()
31297                    {
31298                        part = self.convert_date_part_identifier_expr_to_var(part);
31299                    }
31300                }
31301                Ok(Some(Expression::Function(Box::new(Function {
31302                    name: "DATE_PART".to_string(),
31303                    args: vec![part, from_expr],
31304                    distinct: false,
31305                    trailing_comments: Vec::new(),
31306                    use_bracket_syntax: false,
31307                    no_parens: false,
31308                    quoted: false,
31309                    span: None,
31310                    inferred_type: None,
31311                }))))
31312            }
31313            (crate::function_registry::TypedParseKind::Variadic, "DATEADD") => {
31314                let mut first_arg = self.parse_expression()?;
31315                first_arg = self.try_clickhouse_func_arg_alias(first_arg);
31316                self.expect(TokenType::Comma)?;
31317                let second_arg = self.parse_expression()?;
31318                let second_arg = self.try_clickhouse_func_arg_alias(second_arg);
31319
31320                // Check if there's a third argument (traditional 3-arg syntax)
31321                if self.match_token(TokenType::Comma) {
31322                    let third_arg = self.parse_expression()?;
31323                    let third_arg = self.try_clickhouse_func_arg_alias(third_arg);
31324                    self.expect(TokenType::RParen)?;
31325                    if matches!(
31326                        self.config.dialect,
31327                        Some(crate::dialects::DialectType::Snowflake)
31328                    ) {
31329                        if self
31330                            .try_parse_date_part_unit_identifier_expr(&first_arg)
31331                            .is_some()
31332                        {
31333                            first_arg = self.convert_date_part_identifier_expr_to_var(first_arg);
31334                        }
31335                    }
31336                    Ok(Some(Expression::Function(Box::new(Function {
31337                        name: name.to_string(),
31338                        args: vec![first_arg, second_arg, third_arg],
31339                        distinct: false,
31340                        trailing_comments: Vec::new(),
31341                        use_bracket_syntax: false,
31342                        no_parens: false,
31343                        quoted: false,
31344                        span: None,
31345                        inferred_type: None,
31346                    }))))
31347                } else {
31348                    // BigQuery 2-arg syntax: DATE_ADD(date, interval)
31349                    self.expect(TokenType::RParen)?;
31350                    Ok(Some(Expression::Function(Box::new(Function {
31351                        name: name.to_string(),
31352                        args: vec![first_arg, second_arg],
31353                        distinct: false,
31354                        trailing_comments: Vec::new(),
31355                        use_bracket_syntax: false,
31356                        no_parens: false,
31357                        quoted: false,
31358                        span: None,
31359                        inferred_type: None,
31360                    }))))
31361                }
31362            }
31363            (crate::function_registry::TypedParseKind::Variadic, "DATEDIFF") => {
31364                // First argument (can be unit for DATEDIFF/TIMESTAMPDIFF or datetime for TIMEDIFF)
31365                let first_arg = self.parse_expression()?;
31366                let first_arg = self.try_clickhouse_func_arg_alias(first_arg);
31367                self.expect(TokenType::Comma)?;
31368                let second_arg = self.parse_expression()?;
31369                let second_arg = self.try_clickhouse_func_arg_alias(second_arg);
31370                // Third argument is optional (SQLite TIMEDIFF only takes 2 args)
31371                let mut args = if self.match_token(TokenType::Comma) {
31372                    let third_arg = self.parse_expression()?;
31373                    let third_arg = self.try_clickhouse_func_arg_alias(third_arg);
31374                    vec![first_arg, second_arg, third_arg]
31375                } else {
31376                    vec![first_arg, second_arg]
31377                };
31378                // ClickHouse: optional 4th timezone argument for dateDiff
31379                while self.match_token(TokenType::Comma) {
31380                    let arg = self.parse_expression()?;
31381                    args.push(self.try_clickhouse_func_arg_alias(arg));
31382                }
31383                self.expect(TokenType::RParen)?;
31384                if matches!(
31385                    self.config.dialect,
31386                    Some(crate::dialects::DialectType::Snowflake)
31387                ) && args.len() == 3
31388                {
31389                    if let Some(unit) = self.try_parse_date_part_unit_expr(&args[0]) {
31390                        return Ok(Some(Expression::DateDiff(Box::new(DateDiffFunc {
31391                            this: args[2].clone(),
31392                            expression: args[1].clone(),
31393                            unit: Some(unit),
31394                        }))));
31395                    }
31396                }
31397                Ok(Some(Expression::Function(Box::new(Function {
31398                    name: name.to_string(),
31399                    args,
31400                    distinct: false,
31401                    trailing_comments: Vec::new(),
31402                    use_bracket_syntax: false,
31403                    no_parens: false,
31404                    quoted: false,
31405                    span: None,
31406                    inferred_type: None,
31407                }))))
31408            }
31409            (crate::function_registry::TypedParseKind::Variadic, "RANDOM") => {
31410                // RANDOM() - no args, RANDOM(seed) - Snowflake, RANDOM(lower, upper) - Teradata
31411                if self.check(TokenType::RParen) {
31412                    self.expect(TokenType::RParen)?;
31413                    Ok(Some(Expression::Random(Random)))
31414                } else {
31415                    let first = self.parse_expression()?;
31416                    if self.match_token(TokenType::Comma) {
31417                        let second = self.parse_expression()?;
31418                        self.expect(TokenType::RParen)?;
31419                        Ok(Some(Expression::Rand(Box::new(Rand {
31420                            seed: None,
31421                            lower: Some(Box::new(first)),
31422                            upper: Some(Box::new(second)),
31423                        }))))
31424                    } else {
31425                        self.expect(TokenType::RParen)?;
31426                        Ok(Some(Expression::Rand(Box::new(Rand {
31427                            seed: Some(Box::new(first)),
31428                            lower: None,
31429                            upper: None,
31430                        }))))
31431                    }
31432                }
31433            }
31434            (crate::function_registry::TypedParseKind::Variadic, "RAND") => {
31435                let seed = if self.check(TokenType::RParen) {
31436                    None
31437                } else {
31438                    Some(Box::new(self.parse_expression()?))
31439                };
31440                self.expect(TokenType::RParen)?;
31441                Ok(Some(Expression::Rand(Box::new(Rand {
31442                    seed,
31443                    lower: None,
31444                    upper: None,
31445                }))))
31446            }
31447            (crate::function_registry::TypedParseKind::Variadic, "PI") => {
31448                self.expect(TokenType::RParen)?;
31449                Ok(Some(Expression::Pi(Pi)))
31450            }
31451            (crate::function_registry::TypedParseKind::Variadic, "LAST_DAY") => {
31452                let this = self.parse_expression()?;
31453                let unit = if self.match_token(TokenType::Comma) {
31454                    Some(self.parse_datetime_field()?)
31455                } else {
31456                    None
31457                };
31458                self.expect(TokenType::RParen)?;
31459                Ok(Some(Expression::LastDay(Box::new(LastDayFunc {
31460                    this,
31461                    unit,
31462                }))))
31463            }
31464            (crate::function_registry::TypedParseKind::Variadic, "POSITION") => {
31465                let expr = self
31466                    .parse_position()?
31467                    .ok_or_else(|| self.parse_error("Expected expression in POSITION"))?;
31468                self.expect(TokenType::RParen)?;
31469                Ok(Some(expr))
31470            }
31471            (crate::function_registry::TypedParseKind::Variadic, "STRPOS") => {
31472                let this = self.parse_expression()?;
31473                self.expect(TokenType::Comma)?;
31474                let substr = self.parse_expression()?;
31475                let occurrence = if self.match_token(TokenType::Comma) {
31476                    Some(Box::new(self.parse_expression()?))
31477                } else {
31478                    None
31479                };
31480                self.expect(TokenType::RParen)?;
31481                Ok(Some(Expression::StrPosition(Box::new(StrPosition {
31482                    this: Box::new(this),
31483                    substr: Some(Box::new(substr)),
31484                    position: None,
31485                    occurrence,
31486                }))))
31487            }
31488            (crate::function_registry::TypedParseKind::Variadic, "LOCATE") => {
31489                if self.check(TokenType::RParen) {
31490                    self.skip();
31491                    return Ok(Some(Expression::Function(Box::new(Function {
31492                        name: name.to_string(),
31493                        args: vec![],
31494                        distinct: false,
31495                        trailing_comments: Vec::new(),
31496                        use_bracket_syntax: false,
31497                        no_parens: false,
31498                        quoted: false,
31499                        span: None,
31500                        inferred_type: None,
31501                    }))));
31502                }
31503                let first = self.parse_expression()?;
31504                if !self.check(TokenType::Comma) && self.check(TokenType::RParen) {
31505                    self.skip();
31506                    return Ok(Some(Expression::Function(Box::new(Function {
31507                        name: name.to_string(),
31508                        args: vec![first],
31509                        distinct: false,
31510                        trailing_comments: Vec::new(),
31511                        use_bracket_syntax: false,
31512                        no_parens: false,
31513                        quoted: false,
31514                        span: None,
31515                        inferred_type: None,
31516                    }))));
31517                }
31518                self.expect(TokenType::Comma)?;
31519                let second = self.parse_expression()?;
31520                let position = if self.match_token(TokenType::Comma) {
31521                    Some(Box::new(self.parse_expression()?))
31522                } else {
31523                    None
31524                };
31525                self.expect(TokenType::RParen)?;
31526                Ok(Some(Expression::StrPosition(Box::new(StrPosition {
31527                    this: Box::new(second),
31528                    substr: Some(Box::new(first)),
31529                    position,
31530                    occurrence: None,
31531                }))))
31532            }
31533            (crate::function_registry::TypedParseKind::Variadic, "INSTR") => {
31534                let first = self.parse_expression()?;
31535                self.expect(TokenType::Comma)?;
31536                let second = self.parse_expression()?;
31537                let position = if self.match_token(TokenType::Comma) {
31538                    Some(Box::new(self.parse_expression()?))
31539                } else {
31540                    None
31541                };
31542                self.expect(TokenType::RParen)?;
31543                Ok(Some(Expression::StrPosition(Box::new(StrPosition {
31544                    this: Box::new(first),
31545                    substr: Some(Box::new(second)),
31546                    position,
31547                    occurrence: None,
31548                }))))
31549            }
31550            (crate::function_registry::TypedParseKind::Variadic, "NORMALIZE") => {
31551                let this = self.parse_expression()?;
31552                let form = if self.match_token(TokenType::Comma) {
31553                    Some(Box::new(self.parse_expression()?))
31554                } else {
31555                    None
31556                };
31557                self.expect(TokenType::RParen)?;
31558                Ok(Some(Expression::Normalize(Box::new(Normalize {
31559                    this: Box::new(this),
31560                    form,
31561                    is_casefold: None,
31562                }))))
31563            }
31564            (crate::function_registry::TypedParseKind::Variadic, "INITCAP") => {
31565                let this = self.parse_expression()?;
31566                let delimiter = if self.match_token(TokenType::Comma) {
31567                    Some(Box::new(self.parse_expression()?))
31568                } else {
31569                    None
31570                };
31571                self.expect(TokenType::RParen)?;
31572                if let Some(delim) = delimiter {
31573                    Ok(Some(Expression::Function(Box::new(Function::new(
31574                        "INITCAP".to_string(),
31575                        vec![this, *delim],
31576                    )))))
31577                } else {
31578                    Ok(Some(Expression::Initcap(Box::new(UnaryFunc::new(this)))))
31579                }
31580            }
31581            (crate::function_registry::TypedParseKind::Variadic, "FLOOR") => {
31582                let this = self.parse_expression()?;
31583                let to = if self.match_token(TokenType::To) {
31584                    self.parse_var()?
31585                } else {
31586                    None
31587                };
31588                let scale = if to.is_none() && self.match_token(TokenType::Comma) {
31589                    Some(self.parse_expression()?)
31590                } else {
31591                    None
31592                };
31593                if self.check(TokenType::Comma) {
31594                    let mut args = vec![this];
31595                    if let Some(s) = scale {
31596                        args.push(s);
31597                    }
31598                    while self.match_token(TokenType::Comma) {
31599                        args.push(self.parse_expression()?);
31600                    }
31601                    self.expect(TokenType::RParen)?;
31602                    return Ok(Some(Expression::Function(Box::new(Function {
31603                        name: name.to_string(),
31604                        args,
31605                        distinct: false,
31606                        trailing_comments: Vec::new(),
31607                        use_bracket_syntax: false,
31608                        no_parens: false,
31609                        quoted: false,
31610                        span: None,
31611                        inferred_type: None,
31612                    }))));
31613                }
31614                self.expect(TokenType::RParen)?;
31615                Ok(Some(Expression::Floor(Box::new(FloorFunc {
31616                    this,
31617                    scale,
31618                    to,
31619                }))))
31620            }
31621            (crate::function_registry::TypedParseKind::Variadic, "LOG") => {
31622                let first = self.parse_expression()?;
31623                if self.match_token(TokenType::Comma) {
31624                    let second = self.parse_expression()?;
31625                    self.expect(TokenType::RParen)?;
31626                    let (value, base) = if self.log_base_first() {
31627                        (second, first)
31628                    } else {
31629                        (first, second)
31630                    };
31631                    Ok(Some(Expression::Log(Box::new(LogFunc {
31632                        this: value,
31633                        base: Some(base),
31634                    }))))
31635                } else {
31636                    self.expect(TokenType::RParen)?;
31637                    if self.log_defaults_to_ln() {
31638                        Ok(Some(Expression::Ln(Box::new(UnaryFunc::new(first)))))
31639                    } else {
31640                        Ok(Some(Expression::Log(Box::new(LogFunc {
31641                            this: first,
31642                            base: None,
31643                        }))))
31644                    }
31645                }
31646            }
31647            (crate::function_registry::TypedParseKind::Variadic, "FLATTEN") => {
31648                let args = self.parse_function_arguments()?;
31649                self.expect(TokenType::RParen)?;
31650                Ok(Some(Expression::Function(Box::new(Function {
31651                    name: name.to_string(),
31652                    args,
31653                    distinct: false,
31654                    trailing_comments: Vec::new(),
31655                    use_bracket_syntax: false,
31656                    no_parens: false,
31657                    quoted: false,
31658                    span: None,
31659                    inferred_type: None,
31660                }))))
31661            }
31662            (crate::function_registry::TypedParseKind::Variadic, "ARRAY_INTERSECT") => {
31663                let mut expressions = vec![self.parse_expression()?];
31664                while self.match_token(TokenType::Comma) {
31665                    expressions.push(self.parse_expression()?);
31666                }
31667                self.expect(TokenType::RParen)?;
31668                Ok(Some(Expression::ArrayIntersect(Box::new(VarArgFunc {
31669                    expressions,
31670                    original_name: Some(name.to_string()),
31671                    inferred_type: None,
31672                }))))
31673            }
31674            (crate::function_registry::TypedParseKind::Variadic, "CURRENT_SCHEMAS") => {
31675                let args = if self.check(TokenType::RParen) {
31676                    Vec::new()
31677                } else {
31678                    vec![self.parse_expression()?]
31679                };
31680                self.expect(TokenType::RParen)?;
31681                Ok(Some(Expression::CurrentSchemas(Box::new(CurrentSchemas {
31682                    this: args.into_iter().next().map(Box::new),
31683                }))))
31684            }
31685            (crate::function_registry::TypedParseKind::Variadic, "COALESCE") => {
31686                let args = if self.check(TokenType::RParen) {
31687                    Vec::new()
31688                } else {
31689                    self.parse_expression_list()?
31690                };
31691                self.expect(TokenType::RParen)?;
31692                Ok(Some(Expression::Coalesce(Box::new(
31693                    crate::expressions::VarArgFunc {
31694                        original_name: None,
31695                        expressions: args,
31696                        inferred_type: None,
31697                    },
31698                ))))
31699            }
31700            (crate::function_registry::TypedParseKind::Variadic, "IFNULL") => {
31701                let args = self.parse_expression_list()?;
31702                self.expect(TokenType::RParen)?;
31703                if args.len() >= 2 {
31704                    Ok(Some(Expression::Coalesce(Box::new(
31705                        crate::expressions::VarArgFunc {
31706                            original_name: Some("IFNULL".to_string()),
31707                            expressions: args,
31708                            inferred_type: None,
31709                        },
31710                    ))))
31711                } else {
31712                    Ok(Some(Expression::Function(Box::new(Function {
31713                        name: name.to_string(),
31714                        args,
31715                        distinct: false,
31716                        trailing_comments: Vec::new(),
31717                        use_bracket_syntax: false,
31718                        no_parens: false,
31719                        quoted: false,
31720                        span: None,
31721                        inferred_type: None,
31722                    }))))
31723                }
31724            }
31725            (crate::function_registry::TypedParseKind::Variadic, "NVL") => {
31726                let args = self.parse_expression_list()?;
31727                self.expect(TokenType::RParen)?;
31728                if args.len() > 2 {
31729                    Ok(Some(Expression::Function(Box::new(Function {
31730                        name: "COALESCE".to_string(),
31731                        args,
31732                        distinct: false,
31733                        trailing_comments: Vec::new(),
31734                        use_bracket_syntax: false,
31735                        no_parens: false,
31736                        quoted: false,
31737                        span: None,
31738                        inferred_type: None,
31739                    }))))
31740                } else if args.len() == 2 {
31741                    Ok(Some(Expression::Nvl(Box::new(
31742                        crate::expressions::BinaryFunc {
31743                            original_name: Some("NVL".to_string()),
31744                            this: args[0].clone(),
31745                            expression: args[1].clone(),
31746                            inferred_type: None,
31747                        },
31748                    ))))
31749                } else {
31750                    Ok(Some(Expression::Function(Box::new(Function {
31751                        name: name.to_string(),
31752                        args,
31753                        distinct: false,
31754                        trailing_comments: Vec::new(),
31755                        use_bracket_syntax: false,
31756                        no_parens: false,
31757                        quoted: false,
31758                        span: None,
31759                        inferred_type: None,
31760                    }))))
31761                }
31762            }
31763            (crate::function_registry::TypedParseKind::Variadic, "NVL2") => {
31764                let args = self.parse_expression_list()?;
31765                self.expect(TokenType::RParen)?;
31766                if args.len() >= 3 {
31767                    Ok(Some(Expression::Nvl2(Box::new(
31768                        crate::expressions::Nvl2Func {
31769                            this: args[0].clone(),
31770                            true_value: args[1].clone(),
31771                            false_value: args[2].clone(),
31772                            inferred_type: None,
31773                        },
31774                    ))))
31775                } else {
31776                    Ok(Some(Expression::Function(Box::new(Function {
31777                        name: name.to_string(),
31778                        args,
31779                        distinct: false,
31780                        trailing_comments: Vec::new(),
31781                        use_bracket_syntax: false,
31782                        no_parens: false,
31783                        quoted: false,
31784                        span: None,
31785                        inferred_type: None,
31786                    }))))
31787                }
31788            }
31789            (crate::function_registry::TypedParseKind::Variadic, "EXTRACT") => {
31790                if matches!(
31791                    self.config.dialect,
31792                    Some(crate::dialects::DialectType::ClickHouse)
31793                ) && (self.check(TokenType::Identifier)
31794                    || self.check(TokenType::Var)
31795                    || self.peek().token_type.is_keyword()
31796                    || self.check(TokenType::String)
31797                    || self.check(TokenType::Number))
31798                    && (self.check_next(TokenType::Comma)
31799                        || self.check_next(TokenType::LParen)
31800                        || self.check_next(TokenType::Var)
31801                        || self.check_next(TokenType::Identifier))
31802                {
31803                    let args = self.parse_function_arguments()?;
31804                    self.expect(TokenType::RParen)?;
31805                    return Ok(Some(Expression::Function(Box::new(Function {
31806                        name: name.to_string(),
31807                        args,
31808                        distinct: false,
31809                        trailing_comments: Vec::new(),
31810                        use_bracket_syntax: false,
31811                        no_parens: false,
31812                        quoted: false,
31813                        span: None,
31814                        inferred_type: None,
31815                    }))));
31816                }
31817
31818                if self.check(TokenType::String) {
31819                    let args = self.parse_expression_list()?;
31820                    self.expect(TokenType::RParen)?;
31821                    return Ok(Some(Expression::Function(Box::new(Function {
31822                        name: name.to_string(),
31823                        args,
31824                        distinct: false,
31825                        trailing_comments: Vec::new(),
31826                        use_bracket_syntax: false,
31827                        no_parens: false,
31828                        quoted: false,
31829                        span: None,
31830                        inferred_type: None,
31831                    }))));
31832                }
31833
31834                let field = self.parse_datetime_field()?;
31835                if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
31836                    return Err(self.parse_error("Expected FROM or comma after EXTRACT field"));
31837                }
31838                let this = self.parse_expression()?;
31839                let this = self.try_clickhouse_func_arg_alias(this);
31840                self.expect(TokenType::RParen)?;
31841                Ok(Some(Expression::Extract(Box::new(ExtractFunc {
31842                    this,
31843                    field,
31844                }))))
31845            }
31846            (crate::function_registry::TypedParseKind::Variadic, "STRUCT") => {
31847                let args = if self.check(TokenType::RParen) {
31848                    Vec::new()
31849                } else {
31850                    self.parse_struct_args()?
31851                };
31852                self.expect(TokenType::RParen)?;
31853                Ok(Some(Expression::Function(Box::new(Function {
31854                    name: name.to_string(),
31855                    args,
31856                    distinct: false,
31857                    trailing_comments: Vec::new(),
31858                    use_bracket_syntax: false,
31859                    no_parens: false,
31860                    quoted: false,
31861                    span: None,
31862                    inferred_type: None,
31863                }))))
31864            }
31865            (crate::function_registry::TypedParseKind::Variadic, "CHAR") => {
31866                let args = self.parse_expression_list()?;
31867                let charset = if self.match_token(TokenType::Using) {
31868                    if !self.is_at_end() {
31869                        let charset_token = self.advance();
31870                        Some(charset_token.text.clone())
31871                    } else {
31872                        None
31873                    }
31874                } else {
31875                    None
31876                };
31877                self.expect(TokenType::RParen)?;
31878                if charset.is_some() {
31879                    Ok(Some(Expression::CharFunc(Box::new(
31880                        crate::expressions::CharFunc {
31881                            args,
31882                            charset,
31883                            name: None,
31884                        },
31885                    ))))
31886                } else {
31887                    Ok(Some(Expression::Function(Box::new(Function {
31888                        name: name.to_string(),
31889                        args,
31890                        distinct: false,
31891                        trailing_comments: Vec::new(),
31892                        use_bracket_syntax: false,
31893                        no_parens: false,
31894                        quoted: false,
31895                        span: None,
31896                        inferred_type: None,
31897                    }))))
31898                }
31899            }
31900            (crate::function_registry::TypedParseKind::Variadic, "CHR") => {
31901                let args = self.parse_expression_list()?;
31902                let charset = if self.match_token(TokenType::Using) {
31903                    if !self.is_at_end() {
31904                        let charset_token = self.advance();
31905                        Some(charset_token.text.clone())
31906                    } else {
31907                        None
31908                    }
31909                } else {
31910                    None
31911                };
31912                self.expect(TokenType::RParen)?;
31913                if charset.is_some() {
31914                    Ok(Some(Expression::CharFunc(Box::new(
31915                        crate::expressions::CharFunc {
31916                            args,
31917                            charset,
31918                            name: Some("CHR".to_string()),
31919                        },
31920                    ))))
31921                } else {
31922                    Ok(Some(Expression::Function(Box::new(Function {
31923                        name: name.to_string(),
31924                        args,
31925                        distinct: false,
31926                        trailing_comments: Vec::new(),
31927                        use_bracket_syntax: false,
31928                        no_parens: false,
31929                        quoted: false,
31930                        span: None,
31931                        inferred_type: None,
31932                    }))))
31933                }
31934            }
31935            (crate::function_registry::TypedParseKind::Variadic, "RANGE_N") => {
31936                let this = self.parse_bitwise_or()?;
31937                self.expect(TokenType::Between)?;
31938                let mut expressions = Vec::new();
31939                while !self.check(TokenType::Each) && !self.check(TokenType::RParen) {
31940                    expressions.push(self.parse_expression()?);
31941                    if !self.match_token(TokenType::Comma) {
31942                        break;
31943                    }
31944                }
31945                let each = if self.match_token(TokenType::Each) {
31946                    Some(Box::new(self.parse_expression()?))
31947                } else {
31948                    None
31949                };
31950                self.expect(TokenType::RParen)?;
31951                Ok(Some(Expression::RangeN(Box::new(RangeN {
31952                    this: Box::new(this),
31953                    expressions,
31954                    each,
31955                }))))
31956            }
31957            (crate::function_registry::TypedParseKind::Variadic, "XMLTABLE") => {
31958                if let Some(xml_table) = self.parse_xml_table()? {
31959                    self.expect(TokenType::RParen)?;
31960                    Ok(Some(xml_table))
31961                } else {
31962                    Err(self.parse_error("Failed to parse XMLTABLE"))
31963                }
31964            }
31965            (crate::function_registry::TypedParseKind::Variadic, "XMLELEMENT") => {
31966                if let Some(elem) = self.parse_xml_element()? {
31967                    self.expect(TokenType::RParen)?;
31968                    Ok(Some(elem))
31969                } else {
31970                    self.expect(TokenType::RParen)?;
31971                    Ok(Some(Expression::Function(Box::new(Function {
31972                        name: name.to_string(),
31973                        args: Vec::new(),
31974                        distinct: false,
31975                        trailing_comments: Vec::new(),
31976                        use_bracket_syntax: false,
31977                        no_parens: false,
31978                        quoted: false,
31979                        span: None,
31980                        inferred_type: None,
31981                    }))))
31982                }
31983            }
31984            (crate::function_registry::TypedParseKind::Variadic, "XMLATTRIBUTES") => {
31985                let mut attrs = Vec::new();
31986                if !self.check(TokenType::RParen) {
31987                    loop {
31988                        let expr = self.parse_expression()?;
31989                        if self.match_token(TokenType::As) {
31990                            let alias_ident = self.expect_identifier_or_keyword_with_quoted()?;
31991                            attrs.push(Expression::Alias(Box::new(Alias {
31992                                this: expr,
31993                                alias: alias_ident,
31994                                column_aliases: Vec::new(),
31995                                pre_alias_comments: Vec::new(),
31996                                trailing_comments: Vec::new(),
31997                                inferred_type: None,
31998                            })));
31999                        } else {
32000                            attrs.push(expr);
32001                        }
32002                        if !self.match_token(TokenType::Comma) {
32003                            break;
32004                        }
32005                    }
32006                }
32007                self.expect(TokenType::RParen)?;
32008                Ok(Some(Expression::Function(Box::new(Function {
32009                    name: "XMLATTRIBUTES".to_string(),
32010                    args: attrs,
32011                    distinct: false,
32012                    trailing_comments: Vec::new(),
32013                    use_bracket_syntax: false,
32014                    no_parens: false,
32015                    quoted: false,
32016                    span: None,
32017                    inferred_type: None,
32018                }))))
32019            }
32020            (crate::function_registry::TypedParseKind::Variadic, "XMLCOMMENT") => {
32021                let args = if self.check(TokenType::RParen) {
32022                    Vec::new()
32023                } else {
32024                    self.parse_expression_list()?
32025                };
32026                self.expect(TokenType::RParen)?;
32027                Ok(Some(Expression::Function(Box::new(Function {
32028                    name: "XMLCOMMENT".to_string(),
32029                    args,
32030                    distinct: false,
32031                    trailing_comments: Vec::new(),
32032                    use_bracket_syntax: false,
32033                    no_parens: false,
32034                    quoted: false,
32035                    span: None,
32036                    inferred_type: None,
32037                }))))
32038            }
32039            (crate::function_registry::TypedParseKind::Variadic, "MATCH") => {
32040                let expressions = if self.check(TokenType::Table)
32041                    && !matches!(
32042                        self.config.dialect,
32043                        Some(crate::dialects::DialectType::ClickHouse)
32044                    ) {
32045                    self.skip();
32046                    let table_name = self.expect_identifier_or_keyword()?;
32047                    vec![Expression::Var(Box::new(Var {
32048                        this: format!("TABLE {}", table_name),
32049                    }))]
32050                } else {
32051                    self.parse_expression_list()?
32052                };
32053
32054                self.expect(TokenType::RParen)?;
32055
32056                if !self.check_keyword_text("AGAINST") {
32057                    return Ok(Some(Expression::Function(Box::new(Function {
32058                        name: "MATCH".to_string(),
32059                        args: expressions,
32060                        distinct: false,
32061                        trailing_comments: Vec::new(),
32062                        use_bracket_syntax: false,
32063                        no_parens: false,
32064                        quoted: false,
32065                        span: None,
32066                        inferred_type: None,
32067                    }))));
32068                }
32069
32070                self.skip();
32071                self.expect(TokenType::LParen)?;
32072                let search_expr = self.parse_primary()?;
32073
32074                let modifier = if self.match_text_seq(&["IN", "NATURAL", "LANGUAGE", "MODE"]) {
32075                    if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
32076                        Some(Box::new(Expression::Var(Box::new(Var {
32077                            this: "IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION".to_string(),
32078                        }))))
32079                    } else {
32080                        Some(Box::new(Expression::Var(Box::new(Var {
32081                            this: "IN NATURAL LANGUAGE MODE".to_string(),
32082                        }))))
32083                    }
32084                } else if self.match_text_seq(&["IN", "BOOLEAN", "MODE"]) {
32085                    Some(Box::new(Expression::Var(Box::new(Var {
32086                        this: "IN BOOLEAN MODE".to_string(),
32087                    }))))
32088                } else if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
32089                    Some(Box::new(Expression::Var(Box::new(Var {
32090                        this: "WITH QUERY EXPANSION".to_string(),
32091                    }))))
32092                } else {
32093                    None
32094                };
32095
32096                self.expect(TokenType::RParen)?;
32097
32098                Ok(Some(Expression::MatchAgainst(Box::new(MatchAgainst {
32099                    this: Box::new(search_expr),
32100                    expressions,
32101                    modifier,
32102                }))))
32103            }
32104            (crate::function_registry::TypedParseKind::Variadic, "TRANSFORM") => {
32105                let expressions = if self.check(TokenType::RParen) {
32106                    Vec::new()
32107                } else {
32108                    self.parse_function_args_with_lambda()?
32109                };
32110                self.expect(TokenType::RParen)?;
32111
32112                let row_format_before = if self.match_token(TokenType::Row) {
32113                    self.parse_row()?
32114                } else {
32115                    None
32116                };
32117
32118                let record_writer = if self.match_text_seq(&["RECORDWRITER"]) {
32119                    Some(Box::new(self.parse_expression()?))
32120                } else {
32121                    None
32122                };
32123
32124                if self.match_token(TokenType::Using) {
32125                    let command_script = Some(Box::new(self.parse_expression()?));
32126                    let schema = if self.match_token(TokenType::As) {
32127                        self.parse_schema()?
32128                    } else {
32129                        None
32130                    };
32131
32132                    let row_format_after = if self.match_token(TokenType::Row) {
32133                        self.parse_row()?
32134                    } else {
32135                        None
32136                    };
32137
32138                    let record_reader = if self.match_text_seq(&["RECORDREADER"]) {
32139                        Some(Box::new(self.parse_expression()?))
32140                    } else {
32141                        None
32142                    };
32143
32144                    Ok(Some(Expression::QueryTransform(Box::new(QueryTransform {
32145                        expressions,
32146                        command_script,
32147                        schema: schema.map(Box::new),
32148                        row_format_before: row_format_before.map(Box::new),
32149                        record_writer,
32150                        row_format_after: row_format_after.map(Box::new),
32151                        record_reader,
32152                    }))))
32153                } else {
32154                    Ok(Some(Expression::Function(Box::new(Function {
32155                        name: name.to_string(),
32156                        args: expressions,
32157                        distinct: false,
32158                        trailing_comments: Vec::new(),
32159                        use_bracket_syntax: false,
32160                        no_parens: false,
32161                        quoted,
32162                        span: None,
32163                        inferred_type: None,
32164                    }))))
32165                }
32166            }
32167            (crate::function_registry::TypedParseKind::Variadic, "CONVERT") => {
32168                let is_try = upper_name == "TRY_CONVERT";
32169                let is_tsql = matches!(
32170                    self.config.dialect,
32171                    Some(crate::dialects::DialectType::TSQL)
32172                        | Some(crate::dialects::DialectType::Fabric)
32173                );
32174
32175                if is_tsql {
32176                    let saved = self.current;
32177                    let orig_type_text = if self.current < self.tokens.len() {
32178                        self.tokens[self.current].text.to_ascii_uppercase()
32179                    } else {
32180                        String::new()
32181                    };
32182                    let dt = self.parse_data_type();
32183                    if let Ok(mut dt) = dt {
32184                        if self.match_token(TokenType::Comma) {
32185                            if orig_type_text == "NVARCHAR" || orig_type_text == "NCHAR" {
32186                                dt = match dt {
32187                                    crate::expressions::DataType::VarChar { length, .. } => {
32188                                        if let Some(len) = length {
32189                                            crate::expressions::DataType::Custom {
32190                                                name: format!("{}({})", orig_type_text, len),
32191                                            }
32192                                        } else {
32193                                            crate::expressions::DataType::Custom {
32194                                                name: orig_type_text.clone(),
32195                                            }
32196                                        }
32197                                    }
32198                                    crate::expressions::DataType::Char { length } => {
32199                                        if let Some(len) = length {
32200                                            crate::expressions::DataType::Custom {
32201                                                name: format!("{}({})", orig_type_text, len),
32202                                            }
32203                                        } else {
32204                                            crate::expressions::DataType::Custom {
32205                                                name: orig_type_text.clone(),
32206                                            }
32207                                        }
32208                                    }
32209                                    other => other,
32210                                };
32211                            }
32212                            let value = self.parse_expression()?;
32213                            let style = if self.match_token(TokenType::Comma) {
32214                                Some(self.parse_expression()?)
32215                            } else {
32216                                None
32217                            };
32218                            self.expect(TokenType::RParen)?;
32219                            let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
32220                            let mut args = vec![Expression::DataType(dt), value];
32221                            if let Some(s) = style {
32222                                args.push(s);
32223                            }
32224                            return Ok(Some(Expression::Function(Box::new(Function {
32225                                name: func_name.to_string(),
32226                                args,
32227                                distinct: false,
32228                                trailing_comments: Vec::new(),
32229                                use_bracket_syntax: false,
32230                                no_parens: false,
32231                                quoted: false,
32232                                span: None,
32233                                inferred_type: None,
32234                            }))));
32235                        }
32236                        self.current = saved;
32237                    } else {
32238                        self.current = saved;
32239                    }
32240                }
32241
32242                let this = self.parse_expression()?;
32243                if self.match_token(TokenType::Using) {
32244                    let charset = self.expect_identifier()?;
32245                    self.expect(TokenType::RParen)?;
32246                    Ok(Some(Expression::Cast(Box::new(Cast {
32247                        this,
32248                        to: DataType::CharacterSet { name: charset },
32249                        trailing_comments: Vec::new(),
32250                        double_colon_syntax: false,
32251                        format: None,
32252                        default: None,
32253                        inferred_type: None,
32254                    }))))
32255                } else if self.match_token(TokenType::Comma) {
32256                    let mut args = vec![this];
32257                    args.push(self.parse_expression()?);
32258                    while self.match_token(TokenType::Comma) {
32259                        args.push(self.parse_expression()?);
32260                    }
32261                    self.expect(TokenType::RParen)?;
32262                    let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
32263                    Ok(Some(Expression::Function(Box::new(Function {
32264                        name: func_name.to_string(),
32265                        args,
32266                        distinct: false,
32267                        trailing_comments: Vec::new(),
32268                        use_bracket_syntax: false,
32269                        no_parens: false,
32270                        quoted: false,
32271                        span: None,
32272                        inferred_type: None,
32273                    }))))
32274                } else {
32275                    self.expect(TokenType::RParen)?;
32276                    let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
32277                    Ok(Some(Expression::Function(Box::new(Function {
32278                        name: func_name.to_string(),
32279                        args: vec![this],
32280                        distinct: false,
32281                        trailing_comments: Vec::new(),
32282                        use_bracket_syntax: false,
32283                        no_parens: false,
32284                        quoted: false,
32285                        span: None,
32286                        inferred_type: None,
32287                    }))))
32288                }
32289            }
32290            (crate::function_registry::TypedParseKind::Variadic, "TRIM") => {
32291                let (position, position_explicit) = if self.match_token(TokenType::Leading) {
32292                    (TrimPosition::Leading, true)
32293                } else if self.match_token(TokenType::Trailing) {
32294                    (TrimPosition::Trailing, true)
32295                } else if self.match_token(TokenType::Both) {
32296                    (TrimPosition::Both, true)
32297                } else {
32298                    (TrimPosition::Both, false)
32299                };
32300
32301                if position_explicit || self.check(TokenType::From) {
32302                    if self.match_token(TokenType::From) {
32303                        let this = self.parse_expression()?;
32304                        self.expect(TokenType::RParen)?;
32305                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
32306                            this,
32307                            characters: None,
32308                            position,
32309                            sql_standard_syntax: true,
32310                            position_explicit,
32311                        }))))
32312                    } else {
32313                        let first_expr = self.parse_bitwise_or()?;
32314                        let first_expr = self.try_clickhouse_func_arg_alias(first_expr);
32315                        if self.match_token(TokenType::From) {
32316                            let this = self.parse_bitwise_or()?;
32317                            let this = self.try_clickhouse_func_arg_alias(this);
32318                            self.expect(TokenType::RParen)?;
32319                            Ok(Some(Expression::Trim(Box::new(TrimFunc {
32320                                this,
32321                                characters: Some(first_expr),
32322                                position,
32323                                sql_standard_syntax: true,
32324                                position_explicit,
32325                            }))))
32326                        } else {
32327                            self.expect(TokenType::RParen)?;
32328                            Ok(Some(Expression::Trim(Box::new(TrimFunc {
32329                                this: first_expr,
32330                                characters: None,
32331                                position,
32332                                sql_standard_syntax: true,
32333                                position_explicit,
32334                            }))))
32335                        }
32336                    }
32337                } else {
32338                    let first_expr = self.parse_expression()?;
32339                    let first_expr = self.try_clickhouse_func_arg_alias(first_expr);
32340                    if self.match_token(TokenType::From) {
32341                        let this = self.parse_expression()?;
32342                        let this = self.try_clickhouse_func_arg_alias(this);
32343                        self.expect(TokenType::RParen)?;
32344                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
32345                            this,
32346                            characters: Some(first_expr),
32347                            position: TrimPosition::Both,
32348                            sql_standard_syntax: true,
32349                            position_explicit: false,
32350                        }))))
32351                    } else if self.match_token(TokenType::Comma) {
32352                        let second_expr = self.parse_expression()?;
32353                        self.expect(TokenType::RParen)?;
32354                        let trim_pattern_first = matches!(
32355                            self.config.dialect,
32356                            Some(crate::dialects::DialectType::Spark)
32357                        );
32358                        let (this, characters) = if trim_pattern_first {
32359                            (second_expr, first_expr)
32360                        } else {
32361                            (first_expr, second_expr)
32362                        };
32363                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
32364                            this,
32365                            characters: Some(characters),
32366                            position: TrimPosition::Both,
32367                            sql_standard_syntax: false,
32368                            position_explicit: false,
32369                        }))))
32370                    } else {
32371                        self.expect(TokenType::RParen)?;
32372                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
32373                            this: first_expr,
32374                            characters: None,
32375                            position: TrimPosition::Both,
32376                            sql_standard_syntax: false,
32377                            position_explicit: false,
32378                        }))))
32379                    }
32380                }
32381            }
32382            (crate::function_registry::TypedParseKind::Variadic, "OVERLAY") => {
32383                if matches!(
32384                    self.config.dialect,
32385                    Some(crate::dialects::DialectType::ClickHouse)
32386                ) {
32387                    let args = self.parse_function_arguments()?;
32388                    self.expect(TokenType::RParen)?;
32389                    return Ok(Some(Expression::Function(Box::new(Function {
32390                        name: name.to_string(),
32391                        args,
32392                        distinct: false,
32393                        trailing_comments: Vec::new(),
32394                        use_bracket_syntax: false,
32395                        no_parens: false,
32396                        quoted: false,
32397                        span: None,
32398                        inferred_type: None,
32399                    }))));
32400                }
32401
32402                let this = self.parse_expression()?;
32403                if self.match_token(TokenType::Placing) {
32404                    let replacement = self.parse_expression()?;
32405                    self.expect(TokenType::From)?;
32406                    let from = self.parse_expression()?;
32407                    let length = if self.match_token(TokenType::For) {
32408                        Some(self.parse_expression()?)
32409                    } else {
32410                        None
32411                    };
32412                    self.expect(TokenType::RParen)?;
32413                    Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
32414                        this,
32415                        replacement,
32416                        from,
32417                        length,
32418                    }))))
32419                } else if self.match_token(TokenType::Comma) {
32420                    let replacement = self.parse_expression()?;
32421                    if self.match_token(TokenType::Comma) {
32422                        let from = self.parse_expression()?;
32423                        let length = if self.match_token(TokenType::Comma) {
32424                            Some(self.parse_expression()?)
32425                        } else {
32426                            None
32427                        };
32428                        self.expect(TokenType::RParen)?;
32429                        Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
32430                            this,
32431                            replacement,
32432                            from,
32433                            length,
32434                        }))))
32435                    } else {
32436                        self.expect(TokenType::RParen)?;
32437                        Ok(Some(Expression::Function(Box::new(Function {
32438                            name: name.to_string(),
32439                            args: vec![this, replacement],
32440                            distinct: false,
32441                            trailing_comments: Vec::new(),
32442                            use_bracket_syntax: false,
32443                            no_parens: false,
32444                            quoted: false,
32445                            span: None,
32446                            inferred_type: None,
32447                        }))))
32448                    }
32449                } else {
32450                    self.expect(TokenType::RParen)?;
32451                    Ok(Some(Expression::Function(Box::new(Function {
32452                        name: name.to_string(),
32453                        args: vec![this],
32454                        distinct: false,
32455                        trailing_comments: Vec::new(),
32456                        use_bracket_syntax: false,
32457                        no_parens: false,
32458                        quoted: false,
32459                        span: None,
32460                        inferred_type: None,
32461                    }))))
32462                }
32463            }
32464            (crate::function_registry::TypedParseKind::Variadic, "CEIL") => {
32465                let this = self.parse_expression()?;
32466                // Check for TO unit syntax (Druid: CEIL(__time TO WEEK))
32467                let to = if self.match_token(TokenType::To) {
32468                    // Parse the time unit as a variable/identifier
32469                    self.parse_var()?
32470                } else {
32471                    None
32472                };
32473                let decimals = if to.is_none() && self.match_token(TokenType::Comma) {
32474                    Some(self.parse_expression()?)
32475                } else {
32476                    None
32477                };
32478                self.expect(TokenType::RParen)?;
32479                Ok(Some(Expression::Ceil(Box::new(CeilFunc {
32480                    this,
32481                    decimals,
32482                    to,
32483                }))))
32484            }
32485            (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_FROM_PARTS")
32486            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_NTZ_FROM_PARTS")
32487            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_LTZ_FROM_PARTS")
32488            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_TZ_FROM_PARTS")
32489            | (crate::function_registry::TypedParseKind::Variadic, "DATE_FROM_PARTS")
32490            | (crate::function_registry::TypedParseKind::Variadic, "TIME_FROM_PARTS") => {
32491                let args = self.parse_expression_list()?;
32492                self.expect(TokenType::RParen)?;
32493                Ok(Some(Expression::Function(Box::new(Function {
32494                    name: name.to_string(),
32495                    args,
32496                    distinct: false,
32497                    trailing_comments: Vec::new(),
32498                    use_bracket_syntax: false,
32499                    no_parens: false,
32500                    quoted: false,
32501                    span: None,
32502                    inferred_type: None,
32503                }))))
32504            }
32505            (crate::function_registry::TypedParseKind::CastLike, "TRY_CAST") => {
32506                let this = self.parse_expression()?;
32507                self.expect(TokenType::As)?;
32508                let to = self.parse_data_type()?;
32509                self.expect(TokenType::RParen)?;
32510                Ok(Some(Expression::TryCast(Box::new(Cast {
32511                    this,
32512                    to,
32513                    trailing_comments: Vec::new(),
32514                    double_colon_syntax: false,
32515                    format: None,
32516                    default: None,
32517                    inferred_type: None,
32518                }))))
32519            }
32520            (crate::function_registry::TypedParseKind::Conditional, "IF") => {
32521                // ClickHouse: if() with zero args is valid in test queries
32522                if self.check(TokenType::RParen) {
32523                    self.skip();
32524                    return Ok(Some(Expression::Function(Box::new(Function {
32525                        name: name.to_string(),
32526                        args: vec![],
32527                        distinct: false,
32528                        trailing_comments: Vec::new(),
32529                        use_bracket_syntax: false,
32530                        no_parens: false,
32531                        quoted: false,
32532                        span: None,
32533                        inferred_type: None,
32534                    }))));
32535                }
32536                let args = self.parse_expression_list()?;
32537                self.expect(TokenType::RParen)?;
32538                let expr = if args.len() == 3 {
32539                    Expression::IfFunc(Box::new(crate::expressions::IfFunc {
32540                        original_name: Some(upper_name.to_string()),
32541                        condition: args[0].clone(),
32542                        true_value: args[1].clone(),
32543                        false_value: Some(args[2].clone()),
32544                        inferred_type: None,
32545                    }))
32546                } else if args.len() == 2 {
32547                    // IF with 2 args: condition, true_value (no false_value)
32548                    Expression::IfFunc(Box::new(crate::expressions::IfFunc {
32549                        original_name: Some(upper_name.to_string()),
32550                        condition: args[0].clone(),
32551                        true_value: args[1].clone(),
32552                        false_value: None,
32553                        inferred_type: None,
32554                    }))
32555                } else {
32556                    return Err(self.parse_error("IF function requires 2 or 3 arguments"));
32557                };
32558                Ok(Some(expr))
32559            }
32560            _ => {
32561                self.try_parse_registry_grouped_typed_family(name, upper_name, canonical_upper_name)
32562            }
32563        }
32564    }
32565
32566    /// Route heavy typed-function families via registry metadata groups.
32567    fn try_parse_registry_grouped_typed_family(
32568        &mut self,
32569        name: &str,
32570        upper_name: &str,
32571        canonical_upper_name: &str,
32572    ) -> Result<Option<Expression>> {
32573        use crate::function_registry::TypedDispatchGroup;
32574
32575        match crate::function_registry::typed_dispatch_group_by_name_upper(canonical_upper_name) {
32576            Some(TypedDispatchGroup::AggregateFamily) => self
32577                .parse_typed_aggregate_family(name, upper_name, canonical_upper_name)
32578                .map(Some),
32579            Some(TypedDispatchGroup::WindowFamily) => self
32580                .parse_typed_window_family(name, upper_name, canonical_upper_name)
32581                .map(Some),
32582            Some(TypedDispatchGroup::JsonFamily) => self
32583                .parse_typed_json_family(name, upper_name, canonical_upper_name)
32584                .map(Some),
32585            Some(TypedDispatchGroup::TranslateTeradataFamily) => {
32586                if matches!(
32587                    self.config.dialect,
32588                    Some(crate::dialects::DialectType::Teradata)
32589                ) {
32590                    self.parse_typed_translate_teradata_family(
32591                        name,
32592                        upper_name,
32593                        canonical_upper_name,
32594                    )
32595                    .map(Some)
32596                } else {
32597                    Ok(None)
32598                }
32599            }
32600            None => Ok(None),
32601        }
32602    }
32603
32604    fn make_unquoted_function(name: &str, args: Vec<Expression>) -> Expression {
32605        Expression::Function(Box::new(Function {
32606            name: name.to_string(),
32607            args,
32608            distinct: false,
32609            trailing_comments: Vec::new(),
32610            use_bracket_syntax: false,
32611            no_parens: false,
32612            quoted: false,
32613            span: None,
32614            inferred_type: None,
32615        }))
32616    }
32617
32618    fn make_simple_aggregate(
32619        name: &str,
32620        args: Vec<Expression>,
32621        distinct: bool,
32622        filter: Option<Expression>,
32623    ) -> Expression {
32624        Expression::AggregateFunction(Box::new(AggregateFunction {
32625            name: name.to_string(),
32626            args,
32627            distinct,
32628            filter,
32629            order_by: Vec::new(),
32630            limit: None,
32631            ignore_nulls: None,
32632            inferred_type: None,
32633        }))
32634    }
32635
32636    /// Parse phase-3 typed-function slices that are straightforward pass-throughs.
32637    fn try_parse_phase3_typed_function(
32638        &mut self,
32639        name: &str,
32640        _upper_name: &str,
32641        canonical_upper_name: &str,
32642    ) -> Result<Option<Expression>> {
32643        let Some(behavior) =
32644            crate::function_registry::parser_dispatch_behavior_by_name_upper(canonical_upper_name)
32645        else {
32646            return Ok(None);
32647        };
32648
32649        match behavior {
32650            crate::function_registry::ParserDispatchBehavior::ExprListFunction => {
32651                let args = self.parse_expression_list()?;
32652                self.expect(TokenType::RParen)?;
32653                Ok(Some(Self::make_unquoted_function(name, args)))
32654            }
32655            crate::function_registry::ParserDispatchBehavior::OptionalExprListFunction => {
32656                let args = if self.check(TokenType::RParen) {
32657                    Vec::new()
32658                } else {
32659                    self.parse_expression_list()?
32660                };
32661                self.expect(TokenType::RParen)?;
32662                Ok(Some(Self::make_unquoted_function(name, args)))
32663            }
32664            crate::function_registry::ParserDispatchBehavior::FunctionArgumentsFunction => {
32665                let args = self.parse_function_arguments()?;
32666                self.expect(TokenType::RParen)?;
32667                Ok(Some(Self::make_unquoted_function(name, args)))
32668            }
32669            crate::function_registry::ParserDispatchBehavior::ZeroArgFunction => {
32670                self.expect(TokenType::RParen)?;
32671                Ok(Some(Self::make_unquoted_function(name, Vec::new())))
32672            }
32673            crate::function_registry::ParserDispatchBehavior::ExprListMaybeAggregateByFilter => {
32674                let args = if self.check(TokenType::RParen) {
32675                    Vec::new()
32676                } else {
32677                    self.parse_expression_list()?
32678                };
32679                self.expect(TokenType::RParen)?;
32680                let filter = self.parse_filter_clause()?;
32681                if filter.is_some() {
32682                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
32683                } else {
32684                    Ok(Some(Self::make_unquoted_function(name, args)))
32685                }
32686            }
32687            crate::function_registry::ParserDispatchBehavior::ExprListMaybeAggregateByAggSuffix => {
32688                let args = self.parse_expression_list()?;
32689                self.expect(TokenType::RParen)?;
32690                let filter = self.parse_filter_clause()?;
32691                if canonical_upper_name.ends_with("_AGG") || filter.is_some() {
32692                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
32693                } else {
32694                    Ok(Some(Self::make_unquoted_function(name, args)))
32695                }
32696            }
32697            crate::function_registry::ParserDispatchBehavior::HashLike => {
32698                let args = self.parse_expression_list()?;
32699                self.expect(TokenType::RParen)?;
32700                let filter = self.parse_filter_clause()?;
32701                if canonical_upper_name == "HASH_AGG" || filter.is_some() {
32702                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
32703                } else {
32704                    Ok(Some(Self::make_unquoted_function(name, args)))
32705                }
32706            }
32707            crate::function_registry::ParserDispatchBehavior::HllAggregate => {
32708                let distinct = self.match_token(TokenType::Distinct);
32709                let args = if self.match_token(TokenType::Star) {
32710                    vec![Expression::Star(Star {
32711                        table: None,
32712                        except: None,
32713                        replace: None,
32714                        rename: None,
32715                        trailing_comments: Vec::new(),
32716                        span: None,
32717                    })]
32718                } else if self.check(TokenType::RParen) {
32719                    Vec::new()
32720                } else {
32721                    self.parse_expression_list()?
32722                };
32723                self.expect(TokenType::RParen)?;
32724                let filter = self.parse_filter_clause()?;
32725                Ok(Some(Self::make_simple_aggregate(
32726                    name, args, distinct, filter,
32727                )))
32728            }
32729            crate::function_registry::ParserDispatchBehavior::PercentileAggregate => {
32730                let distinct = self.match_token(TokenType::Distinct);
32731                if !distinct {
32732                    self.match_token(TokenType::All);
32733                }
32734                let args = self.parse_expression_list()?;
32735                self.expect(TokenType::RParen)?;
32736                let filter = self.parse_filter_clause()?;
32737                Ok(Some(Self::make_simple_aggregate(
32738                    name, args, distinct, filter,
32739                )))
32740            }
32741            crate::function_registry::ParserDispatchBehavior::ExprListAggregate => {
32742                let args = self.parse_expression_list()?;
32743                self.expect(TokenType::RParen)?;
32744                let filter = self.parse_filter_clause()?;
32745                Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
32746            }
32747            crate::function_registry::ParserDispatchBehavior::UnaryAggregate => {
32748                let this = self.parse_expression()?;
32749                self.expect(TokenType::RParen)?;
32750                let filter = self.parse_filter_clause()?;
32751                Ok(Some(Self::make_simple_aggregate(
32752                    name,
32753                    vec![this],
32754                    false,
32755                    filter,
32756                )))
32757            }
32758            crate::function_registry::ParserDispatchBehavior::TranslateNonTeradata => {
32759                if matches!(
32760                    self.config.dialect,
32761                    Some(crate::dialects::DialectType::Teradata)
32762                ) {
32763                    return Ok(None);
32764                }
32765                let args = self.parse_expression_list()?;
32766                self.expect(TokenType::RParen)?;
32767                Ok(Some(Self::make_unquoted_function(name, args)))
32768            }
32769        }
32770    }
32771
32772    /// Parse a typed function call (after the opening paren)
32773    /// Following Python SQLGlot pattern: match all function aliases to typed expressions
32774    fn parse_typed_function(
32775        &mut self,
32776        name: &str,
32777        upper_name: &str,
32778        quoted: bool,
32779    ) -> Result<Expression> {
32780        let canonical_upper_name =
32781            crate::function_registry::canonical_typed_function_name_upper(upper_name);
32782
32783        // Handle internal function rewrites (sqlglot internal functions that map to CAST)
32784        if canonical_upper_name == "TIME_TO_TIME_STR" {
32785            let arg = self.parse_expression()?;
32786            self.expect(TokenType::RParen)?;
32787            return Ok(Expression::Cast(Box::new(Cast {
32788                this: arg,
32789                to: DataType::Text,
32790                trailing_comments: Vec::new(),
32791                double_colon_syntax: false,
32792                format: None,
32793                default: None,
32794                inferred_type: None,
32795            })));
32796        }
32797
32798        if let Some(expr) =
32799            self.try_parse_registry_typed_function(name, upper_name, canonical_upper_name, quoted)?
32800        {
32801            return Ok(expr);
32802        }
32803        if let Some(expr) =
32804            self.try_parse_phase3_typed_function(name, upper_name, canonical_upper_name)?
32805        {
32806            return Ok(expr);
32807        }
32808
32809        self.parse_generic_function(name, quoted)
32810    }
32811
32812    fn parse_typed_aggregate_family(
32813        &mut self,
32814        name: &str,
32815        upper_name: &str,
32816        canonical_upper_name: &str,
32817    ) -> Result<Expression> {
32818        match canonical_upper_name {
32819            // COUNT function
32820            "COUNT" => {
32821                let (this, star, distinct) = if self.check(TokenType::RParen) {
32822                    (None, false, false)
32823                } else if self.match_token(TokenType::Star) {
32824                    (None, true, false)
32825                } else if self.match_token(TokenType::All) {
32826                    // COUNT(ALL expr) - ALL is the default, just consume it
32827                    (Some(self.parse_expression()?), false, false)
32828                } else if self.match_token(TokenType::Distinct) {
32829                    let first_expr = self.parse_expression()?;
32830                    // Check for multiple columns: COUNT(DISTINCT a, b, c)
32831                    if self.match_token(TokenType::Comma) {
32832                        let mut args = vec![first_expr];
32833                        loop {
32834                            args.push(self.parse_expression()?);
32835                            if !self.match_token(TokenType::Comma) {
32836                                break;
32837                            }
32838                        }
32839                        // Return as a tuple expression for COUNT DISTINCT over multiple columns
32840                        (
32841                            Some(Expression::Tuple(Box::new(Tuple { expressions: args }))),
32842                            false,
32843                            true,
32844                        )
32845                    } else {
32846                        (Some(first_expr), false, true)
32847                    }
32848                } else {
32849                    let first_expr = self.parse_expression()?;
32850                    // ClickHouse: consume optional AS alias inside function args (e.g., count(NULL AS a))
32851                    let first_expr = if matches!(
32852                        self.config.dialect,
32853                        Some(crate::dialects::DialectType::ClickHouse)
32854                    ) && self.check(TokenType::As)
32855                    {
32856                        self.skip(); // consume AS
32857                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
32858                        Expression::Alias(Box::new(Alias {
32859                            this: first_expr,
32860                            alias,
32861                            column_aliases: Vec::new(),
32862                            pre_alias_comments: Vec::new(),
32863                            trailing_comments: Vec::new(),
32864                            inferred_type: None,
32865                        }))
32866                    } else {
32867                        first_expr
32868                    };
32869                    // Check for multiple arguments (rare but possible)
32870                    if self.match_token(TokenType::Comma) {
32871                        let mut args = vec![first_expr];
32872                        loop {
32873                            args.push(self.parse_expression()?);
32874                            if !self.match_token(TokenType::Comma) {
32875                                break;
32876                            }
32877                        }
32878                        self.expect(TokenType::RParen)?;
32879                        // Multiple args without DISTINCT - treat as generic function
32880                        return Ok(Expression::Function(Box::new(Function {
32881                            name: name.to_string(),
32882                            args,
32883                            distinct: false,
32884                            trailing_comments: Vec::new(),
32885                            use_bracket_syntax: false,
32886                            no_parens: false,
32887                            quoted: false,
32888                            span: None,
32889                            inferred_type: None,
32890                        })));
32891                    }
32892                    (Some(first_expr), false, false)
32893                };
32894                // BigQuery: RESPECT NULLS / IGNORE NULLS inside COUNT
32895                let ignore_nulls = if self.match_token(TokenType::Ignore)
32896                    && self.match_token(TokenType::Nulls)
32897                {
32898                    Some(true)
32899                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
32900                {
32901                    Some(false)
32902                } else {
32903                    None
32904                };
32905                self.expect(TokenType::RParen)?;
32906                let filter = self.parse_filter_clause()?;
32907                // Also check for IGNORE NULLS / RESPECT NULLS after the closing paren
32908                let ignore_nulls = if ignore_nulls.is_some() {
32909                    ignore_nulls
32910                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
32911                    Some(true)
32912                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
32913                    Some(false)
32914                } else {
32915                    None
32916                };
32917                Ok(Expression::Count(Box::new(CountFunc {
32918                    this,
32919                    star,
32920                    distinct,
32921                    filter,
32922                    ignore_nulls,
32923                    original_name: Some(name.to_string()),
32924                    inferred_type: None,
32925                })))
32926            }
32927
32928            // LIST function: LIST(SELECT ...) in Materialize - list constructor with subquery
32929            "LIST" => {
32930                let is_materialize = matches!(
32931                    self.config.dialect,
32932                    Some(crate::dialects::DialectType::Materialize)
32933                );
32934                if is_materialize && self.check(TokenType::Select) {
32935                    let query = self.parse_select()?;
32936                    self.expect(TokenType::RParen)?;
32937                    return Ok(Expression::List(Box::new(List {
32938                        expressions: vec![query],
32939                    })));
32940                }
32941                // For non-Materialize or non-subquery, parse as either generic function or aggregate.
32942                let distinct = self.match_token(TokenType::Distinct);
32943                let args = if self.check(TokenType::RParen) {
32944                    Vec::new()
32945                } else {
32946                    self.parse_function_arguments()?
32947                };
32948                let order_by = if self.match_token(TokenType::Order) {
32949                    self.expect(TokenType::By)?;
32950                    self.parse_order_by_list()?
32951                } else {
32952                    Vec::new()
32953                };
32954                let limit = if self.match_token(TokenType::Limit) {
32955                    Some(Box::new(self.parse_expression()?))
32956                } else {
32957                    None
32958                };
32959                self.expect(TokenType::RParen)?;
32960                let filter = self.parse_filter_clause()?;
32961
32962                if distinct || !order_by.is_empty() || limit.is_some() || filter.is_some() {
32963                    Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
32964                        name: name.to_string(),
32965                        args,
32966                        distinct,
32967                        filter,
32968                        order_by,
32969                        limit,
32970                        ignore_nulls: None,
32971                        inferred_type: None,
32972                    })))
32973                } else {
32974                    Ok(Expression::Function(Box::new(Function {
32975                        name: name.to_string(),
32976                        args,
32977                        distinct: false,
32978                        trailing_comments: Vec::new(),
32979                        use_bracket_syntax: false,
32980                        no_parens: false,
32981                        quoted: false,
32982                        span: None,
32983                        inferred_type: None,
32984                    })))
32985                }
32986            }
32987
32988            // MAP function: MAP(SELECT ...) in Materialize - map constructor with subquery
32989            "MAP" => {
32990                let is_materialize = matches!(
32991                    self.config.dialect,
32992                    Some(crate::dialects::DialectType::Materialize)
32993                );
32994                if is_materialize && self.check(TokenType::Select) {
32995                    let query = self.parse_select()?;
32996                    self.expect(TokenType::RParen)?;
32997                    return Ok(Expression::ToMap(Box::new(ToMap {
32998                        this: Box::new(query),
32999                    })));
33000                }
33001                // For non-Materialize or non-subquery, fall through to generic handling
33002                let args = if self.check(TokenType::RParen) {
33003                    Vec::new()
33004                } else {
33005                    self.parse_function_arguments()?
33006                };
33007                self.expect(TokenType::RParen)?;
33008                Ok(Expression::Function(Box::new(Function {
33009                    name: name.to_string(),
33010                    args,
33011                    distinct: false,
33012                    trailing_comments: Vec::new(),
33013                    use_bracket_syntax: false,
33014                    no_parens: false,
33015                    quoted: false,
33016                    span: None,
33017                    inferred_type: None,
33018                })))
33019            }
33020
33021            // ARRAY function: ARRAY(SELECT ...) or ARRAY((SELECT ...) LIMIT n) is an array constructor with subquery
33022            // Different from ARRAY<type> which is a data type
33023            "ARRAY" => {
33024                // Check if this is ARRAY(SELECT ...) - array subquery constructor
33025                if self.check(TokenType::Select) {
33026                    let query = self.parse_select()?;
33027                    self.expect(TokenType::RParen)?;
33028                    // Pass the query directly as an argument to ARRAY function
33029                    // The generator will handle it correctly
33030                    return Ok(Expression::Function(Box::new(Function {
33031                        name: name.to_string(),
33032                        args: vec![query],
33033                        distinct: false,
33034                        trailing_comments: Vec::new(),
33035                        use_bracket_syntax: false,
33036                        no_parens: false,
33037                        quoted: false,
33038                        span: None,
33039                        inferred_type: None,
33040                    })));
33041                }
33042                // Check if this is ARRAY((SELECT ...) LIMIT n) - BigQuery allows LIMIT outside the subquery parens
33043                // This is common for constructs like ARRAY((SELECT AS STRUCT ...) LIMIT 10)
33044                if self.check(TokenType::LParen) {
33045                    // This could be a parenthesized subquery with modifiers after it
33046                    // Save position in case we need to backtrack
33047                    let saved_pos = self.current;
33048                    self.skip(); // consume opening paren
33049
33050                    // Check if there's a SELECT or WITH inside
33051                    if self.check(TokenType::Select) || self.check(TokenType::With) {
33052                        let inner_query = self.parse_statement()?;
33053                        self.expect(TokenType::RParen)?; // close inner parens
33054
33055                        // Now check for LIMIT/OFFSET modifiers outside the inner parens
33056                        let limit = if self.match_token(TokenType::Limit) {
33057                            let expr = self.parse_expression()?;
33058                            Some(Limit {
33059                                this: expr,
33060                                percent: false,
33061                                comments: Vec::new(),
33062                            })
33063                        } else {
33064                            None
33065                        };
33066
33067                        let offset = if self.match_token(TokenType::Offset) {
33068                            let expr = self.parse_expression()?;
33069                            let rows = if self.match_token(TokenType::Row)
33070                                || self.match_token(TokenType::Rows)
33071                            {
33072                                Some(true)
33073                            } else {
33074                                None
33075                            };
33076                            Some(Offset { this: expr, rows })
33077                        } else {
33078                            None
33079                        };
33080
33081                        self.expect(TokenType::RParen)?; // close ARRAY parens
33082
33083                        // Wrap the inner query in a Subquery with the modifiers
33084                        let subquery = Expression::Subquery(Box::new(Subquery {
33085                            this: inner_query,
33086                            alias: None,
33087                            column_aliases: Vec::new(),
33088                            order_by: None,
33089                            limit,
33090                            offset,
33091                            lateral: false,
33092                            modifiers_inside: false,
33093                            trailing_comments: Vec::new(),
33094                            distribute_by: None,
33095                            sort_by: None,
33096                            cluster_by: None,
33097                            inferred_type: None,
33098                        }));
33099
33100                        return Ok(Expression::Function(Box::new(Function {
33101                            name: name.to_string(),
33102                            args: vec![subquery],
33103                            distinct: false,
33104                            trailing_comments: Vec::new(),
33105                            use_bracket_syntax: false,
33106                            no_parens: false,
33107                            quoted: false,
33108                            span: None,
33109                            inferred_type: None,
33110                        })));
33111                    } else {
33112                        // Not a subquery, backtrack and parse as regular arguments
33113                        self.current = saved_pos;
33114                    }
33115                }
33116                // Otherwise fall through to parse as generic function or error
33117                // This could be ARRAY(...values...) or invalid syntax
33118                let args = if self.check(TokenType::RParen) {
33119                    Vec::new()
33120                } else {
33121                    self.parse_function_arguments()?
33122                };
33123                self.expect(TokenType::RParen)?;
33124                Ok(Expression::Function(Box::new(Function {
33125                    name: name.to_string(),
33126                    args,
33127                    distinct: false,
33128                    trailing_comments: Vec::new(),
33129                    use_bracket_syntax: false,
33130                    no_parens: false,
33131                    quoted: false,
33132                    span: None,
33133                    inferred_type: None,
33134                })))
33135            }
33136
33137            // Simple aggregate functions (SUM, AVG, MIN, MAX, etc.)
33138            // These can have multiple arguments in some contexts (e.g., MAX(a, b) is a scalar function)
33139            "SUM"
33140            | "AVG"
33141            | "MIN"
33142            | "MAX"
33143            | "ARRAY_AGG"
33144            | "ARRAY_CONCAT_AGG"
33145            | "STDDEV"
33146            | "STDDEV_POP"
33147            | "STDDEV_SAMP"
33148            | "VARIANCE"
33149            | "VAR_POP"
33150            | "VAR_SAMP"
33151            | "MEDIAN"
33152            | "MODE"
33153            | "FIRST"
33154            | "LAST"
33155            | "ANY_VALUE"
33156            | "APPROX_DISTINCT"
33157            | "APPROX_COUNT_DISTINCT"
33158            | "BIT_AND"
33159            | "BIT_OR"
33160            | "BIT_XOR" => {
33161                let distinct = if self.match_token(TokenType::Distinct) {
33162                    true
33163                } else {
33164                    self.match_token(TokenType::All); // ALL is the default, just consume it
33165                    false
33166                };
33167
33168                // MODE() can have zero arguments when used with WITHIN GROUP
33169                // e.g., MODE() WITHIN GROUP (ORDER BY col)
33170                if self.check(TokenType::RParen) {
33171                    // Empty args - will likely be followed by WITHIN GROUP
33172                    self.expect(TokenType::RParen)?;
33173                    let filter = self.parse_filter_clause()?;
33174                    let agg = AggFunc {
33175                        ignore_nulls: None,
33176                        this: Expression::Null(Null {}), // Placeholder for 0-arg aggregate
33177                        distinct: false,
33178                        filter,
33179                        order_by: Vec::new(),
33180                        having_max: None,
33181                        name: Some(name.to_string()),
33182                        limit: None,
33183                        inferred_type: None,
33184                    };
33185                    return Ok(match upper_name {
33186                        "MODE" => Expression::Mode(Box::new(agg)),
33187                        _ => {
33188                            // ClickHouse: allow zero-arg aggregates (server will validate)
33189                            if matches!(
33190                                self.config.dialect,
33191                                Some(crate::dialects::DialectType::ClickHouse)
33192                            ) {
33193                                Expression::Function(Box::new(Function {
33194                                    name: name.to_string(),
33195                                    args: Vec::new(),
33196                                    distinct: false,
33197                                    trailing_comments: Vec::new(),
33198                                    use_bracket_syntax: false,
33199                                    no_parens: false,
33200                                    quoted: false,
33201                                    span: None,
33202                                    inferred_type: None,
33203                                }))
33204                            } else {
33205                                return Err(self.parse_error(format!(
33206                                    "{} cannot have zero arguments",
33207                                    upper_name
33208                                )));
33209                            }
33210                        }
33211                    });
33212                }
33213
33214                let first_arg = self.parse_expression_with_clickhouse_alias()?;
33215
33216                // Check if there are more arguments (multi-arg scalar function like MAX(a, b))
33217                if self.match_token(TokenType::Comma) {
33218                    // Special handling for FIRST, LAST, ANY_VALUE with boolean second arg
33219                    // In Spark/Hive: first(col, true) means FIRST(col) IGNORE NULLS
33220                    let is_ignore_nulls_func = matches!(upper_name, "FIRST" | "LAST" | "ANY_VALUE");
33221
33222                    let second_arg = self.parse_expression()?;
33223
33224                    // Check if this is the IGNORE NULLS pattern: func(col, true)
33225                    if is_ignore_nulls_func && self.check(TokenType::RParen) {
33226                        if let Expression::Boolean(BooleanLiteral { value: true }) = &second_arg {
33227                            // This is func(col, true) -> FUNC(col) IGNORE NULLS
33228                            self.expect(TokenType::RParen)?;
33229                            let filter = self.parse_filter_clause()?;
33230                            let agg = AggFunc {
33231                                ignore_nulls: Some(true),
33232                                this: first_arg,
33233                                distinct,
33234                                filter,
33235                                order_by: Vec::new(),
33236                                having_max: None,
33237                                name: Some(name.to_string()),
33238                                limit: None,
33239                                inferred_type: None,
33240                            };
33241                            return Ok(match upper_name {
33242                                "FIRST" => Expression::First(Box::new(agg)),
33243                                "LAST" => Expression::Last(Box::new(agg)),
33244                                "ANY_VALUE" => Expression::AnyValue(Box::new(agg)),
33245                                _ => unreachable!(
33246                                    "function name already matched by is_ignore_nulls_func guard"
33247                                ),
33248                            });
33249                        }
33250                    }
33251
33252                    // Multiple arguments - treat as generic function call
33253                    let mut args = vec![first_arg, second_arg];
33254                    while self.match_token(TokenType::Comma) {
33255                        args.push(self.parse_expression()?);
33256                    }
33257                    self.expect(TokenType::RParen)?;
33258                    Ok(Expression::Function(Box::new(Function {
33259                        name: name.to_string(),
33260                        args,
33261                        distinct: false,
33262                        trailing_comments: Vec::new(),
33263                        use_bracket_syntax: false,
33264                        no_parens: false,
33265                        quoted: false,
33266                        span: None,
33267                        inferred_type: None,
33268                    })))
33269                } else {
33270                    // Check for IGNORE NULLS / RESPECT NULLS (BigQuery style)
33271                    let ignore_nulls = if self.match_token(TokenType::Ignore)
33272                        && self.match_token(TokenType::Nulls)
33273                    {
33274                        Some(true)
33275                    } else if self.match_token(TokenType::Respect)
33276                        && self.match_token(TokenType::Nulls)
33277                    {
33278                        Some(false)
33279                    } else {
33280                        None
33281                    };
33282
33283                    // Check for HAVING MAX/MIN inside aggregate (BigQuery syntax)
33284                    // e.g., ANY_VALUE(fruit HAVING MAX sold)
33285                    let having_max = if self.match_token(TokenType::Having) {
33286                        let is_max = if self.check_keyword_text("MAX") {
33287                            self.skip();
33288                            true
33289                        } else if self.check_keyword_text("MIN") {
33290                            self.skip();
33291                            false
33292                        } else {
33293                            return Err(
33294                                self.parse_error("Expected MAX or MIN after HAVING in aggregate")
33295                            );
33296                        };
33297                        let expr = self.parse_expression()?;
33298                        Some((Box::new(expr), is_max))
33299                    } else {
33300                        None
33301                    };
33302
33303                    // Check for ORDER BY inside aggregate (e.g., ARRAY_AGG(x ORDER BY y))
33304                    let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33305                        self.parse_order_by_list()?
33306                    } else {
33307                        Vec::new()
33308                    };
33309                    // Check for LIMIT inside aggregate (e.g., ARRAY_AGG(x ORDER BY y LIMIT 2))
33310                    // Also supports LIMIT offset, count (e.g., ARRAY_AGG(x ORDER BY y LIMIT 1, 10))
33311                    let limit = if self.match_token(TokenType::Limit) {
33312                        let first = self.parse_expression()?;
33313                        if self.match_token(TokenType::Comma) {
33314                            let second = self.parse_expression()?;
33315                            // Store as Tuple(offset, count)
33316                            Some(Box::new(Expression::Tuple(Box::new(Tuple {
33317                                expressions: vec![first, second],
33318                            }))))
33319                        } else {
33320                            Some(Box::new(first))
33321                        }
33322                    } else {
33323                        None
33324                    };
33325                    // Single argument - treat as aggregate function
33326                    self.expect(TokenType::RParen)?;
33327                    let filter = self.parse_filter_clause()?;
33328                    // Also check for IGNORE NULLS / RESPECT NULLS after the closing paren
33329                    // e.g., FIRST(col) IGNORE NULLS (Hive/Spark/generic SQL syntax)
33330                    let ignore_nulls = if ignore_nulls.is_some() {
33331                        ignore_nulls
33332                    } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33333                        Some(true)
33334                    } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33335                        Some(false)
33336                    } else {
33337                        None
33338                    };
33339                    let agg = AggFunc {
33340                        ignore_nulls,
33341                        this: first_arg,
33342                        distinct,
33343                        filter,
33344                        order_by,
33345                        having_max,
33346                        name: Some(name.to_string()),
33347                        limit,
33348                        inferred_type: None,
33349                    };
33350                    Ok(match upper_name {
33351                        "SUM" => Expression::Sum(Box::new(agg)),
33352                        "AVG" => Expression::Avg(Box::new(agg)),
33353                        "MIN" => Expression::Min(Box::new(agg)),
33354                        "MAX" => Expression::Max(Box::new(agg)),
33355                        "ARRAY_AGG" => Expression::ArrayAgg(Box::new(agg)),
33356                        "ARRAY_CONCAT_AGG" => Expression::ArrayConcatAgg(Box::new(agg)),
33357                        "STDDEV" => Expression::Stddev(Box::new(agg)),
33358                        "STDDEV_POP" => Expression::StddevPop(Box::new(agg)),
33359                        "STDDEV_SAMP" => Expression::StddevSamp(Box::new(agg)),
33360                        "VARIANCE" => Expression::Variance(Box::new(agg)),
33361                        "VAR_POP" => Expression::VarPop(Box::new(agg)),
33362                        "VAR_SAMP" => Expression::VarSamp(Box::new(agg)),
33363                        "MEDIAN" => Expression::Median(Box::new(agg)),
33364                        "MODE" => Expression::Mode(Box::new(agg)),
33365                        "FIRST" => Expression::First(Box::new(agg)),
33366                        "LAST" => Expression::Last(Box::new(agg)),
33367                        "ANY_VALUE" => Expression::AnyValue(Box::new(agg)),
33368                        "APPROX_DISTINCT" => Expression::ApproxDistinct(Box::new(agg)),
33369                        "APPROX_COUNT_DISTINCT" => Expression::ApproxCountDistinct(Box::new(agg)),
33370                        "BIT_AND" => Expression::BitwiseAndAgg(Box::new(agg)),
33371                        "BIT_OR" => Expression::BitwiseOrAgg(Box::new(agg)),
33372                        "BIT_XOR" => Expression::BitwiseXorAgg(Box::new(agg)),
33373                        _ => unreachable!("aggregate function name already matched in caller"),
33374                    })
33375                }
33376            }
33377
33378            // STRING_AGG - STRING_AGG([DISTINCT] expr [, separator] [ORDER BY order_list])
33379            "STRING_AGG" => {
33380                let distinct = self.match_token(TokenType::Distinct);
33381                let this = self.parse_expression()?;
33382                // Separator is optional
33383                let separator = if self.match_token(TokenType::Comma) {
33384                    Some(self.parse_expression()?)
33385                } else {
33386                    None
33387                };
33388                let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33389                    Some(self.parse_order_by_list()?)
33390                } else {
33391                    None
33392                };
33393                // BigQuery: LIMIT inside STRING_AGG
33394                let limit = if self.match_token(TokenType::Limit) {
33395                    Some(Box::new(self.parse_expression()?))
33396                } else {
33397                    None
33398                };
33399                self.expect(TokenType::RParen)?;
33400                let filter = self.parse_filter_clause()?;
33401                Ok(Expression::StringAgg(Box::new(StringAggFunc {
33402                    this,
33403                    separator,
33404                    order_by,
33405                    distinct,
33406                    filter,
33407                    limit,
33408                    inferred_type: None,
33409                })))
33410            }
33411
33412            // GROUP_CONCAT - GROUP_CONCAT([DISTINCT] expr [, expr...] [ORDER BY order_list] [SEPARATOR 'sep'])
33413            // MySQL allows multiple args which get wrapped in CONCAT:
33414            // GROUP_CONCAT(a, b, c SEPARATOR ',') -> GroupConcat(CONCAT(a, b, c), SEPARATOR=',')
33415            "GROUP_CONCAT" => {
33416                let distinct = self.match_token(TokenType::Distinct);
33417                let first = self.parse_expression()?;
33418                // Check for additional comma-separated expressions (before ORDER BY or SEPARATOR)
33419                let mut exprs = vec![first];
33420                while self.match_token(TokenType::Comma) {
33421                    // Check if the next tokens are ORDER BY or SEPARATOR
33422                    // If so, the comma was part of the separator syntax (not more args)
33423                    if self.check(TokenType::Order) || self.check(TokenType::Separator) {
33424                        // This shouldn't happen normally in valid SQL, backtrack
33425                        break;
33426                    }
33427                    exprs.push(self.parse_expression()?);
33428                }
33429                // If multiple expressions, wrap in CONCAT (matches Python sqlglot behavior)
33430                let this = if exprs.len() == 1 {
33431                    exprs.pop().unwrap()
33432                } else {
33433                    Expression::Function(Box::new(Function::new("CONCAT".to_string(), exprs)))
33434                };
33435                // Parse optional ORDER BY
33436                let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33437                    Some(self.parse_order_by_list()?)
33438                } else {
33439                    None
33440                };
33441                // Parse optional SEPARATOR - can be a string literal or expression (e.g., variable)
33442                let separator = if self.match_token(TokenType::Separator) {
33443                    Some(self.parse_expression()?)
33444                } else {
33445                    None
33446                };
33447                self.expect(TokenType::RParen)?;
33448                let filter = self.parse_filter_clause()?;
33449                Ok(Expression::GroupConcat(Box::new(GroupConcatFunc {
33450                    this,
33451                    separator,
33452                    order_by,
33453                    distinct,
33454                    filter,
33455                    inferred_type: None,
33456                })))
33457            }
33458
33459            // LISTAGG - LISTAGG([DISTINCT] expr [, separator [ON OVERFLOW ...]]) WITHIN GROUP (ORDER BY ...)
33460            "LISTAGG" => {
33461                // Check for optional DISTINCT
33462                let distinct = self.match_token(TokenType::Distinct);
33463                let this = self.parse_expression()?;
33464                let separator = if self.match_token(TokenType::Comma) {
33465                    Some(self.parse_expression()?)
33466                } else {
33467                    None
33468                };
33469                // Parse optional ON OVERFLOW clause
33470                let on_overflow = if self.match_token(TokenType::On) {
33471                    if self.match_identifier("OVERFLOW") {
33472                        if self.match_identifier("ERROR") {
33473                            Some(ListAggOverflow::Error)
33474                        } else if self.match_token(TokenType::Truncate) {
33475                            // Optional filler string
33476                            let filler = if self.check(TokenType::String) {
33477                                Some(self.parse_expression()?)
33478                            } else {
33479                                None
33480                            };
33481                            // WITH COUNT or WITHOUT COUNT
33482                            let with_count = if self.match_token(TokenType::With) {
33483                                self.match_identifier("COUNT");
33484                                true
33485                            } else if self.match_identifier("WITHOUT") {
33486                                self.match_identifier("COUNT");
33487                                false
33488                            } else {
33489                                true // default is WITH COUNT
33490                            };
33491                            Some(ListAggOverflow::Truncate { filler, with_count })
33492                        } else {
33493                            None
33494                        }
33495                    } else {
33496                        None
33497                    }
33498                } else {
33499                    None
33500                };
33501                self.expect(TokenType::RParen)?;
33502                // WITHIN GROUP (ORDER BY ...) is handled by maybe_parse_over
33503                Ok(Expression::ListAgg(Box::new(ListAggFunc {
33504                    this,
33505                    separator,
33506                    on_overflow,
33507                    order_by: None,
33508                    distinct,
33509                    filter: None,
33510                    inferred_type: None,
33511                })))
33512            }
33513            _ => unreachable!(
33514                "phase-6 aggregate parser called with non-aggregate family name '{}'",
33515                canonical_upper_name
33516            ),
33517        }
33518    }
33519
33520    fn parse_typed_window_family(
33521        &mut self,
33522        name: &str,
33523        upper_name: &str,
33524        canonical_upper_name: &str,
33525    ) -> Result<Expression> {
33526        match canonical_upper_name {
33527            // Window functions with no arguments (ClickHouse allows args in row_number)
33528            "ROW_NUMBER" => {
33529                if self.check(TokenType::RParen) {
33530                    self.skip();
33531                    Ok(Expression::RowNumber(RowNumber))
33532                } else {
33533                    // ClickHouse: row_number(column1) — parse as regular function
33534                    let args = self.parse_function_args_list()?;
33535                    self.expect(TokenType::RParen)?;
33536                    let trailing_comments = self.previous_trailing_comments().to_vec();
33537                    Ok(Expression::Function(Box::new(Function {
33538                        name: name.to_string(),
33539                        args,
33540                        distinct: false,
33541                        trailing_comments,
33542                        use_bracket_syntax: false,
33543                        no_parens: false,
33544                        quoted: false,
33545                        span: None,
33546                        inferred_type: None,
33547                    })))
33548                }
33549            }
33550            "RANK" => {
33551                // DuckDB allows: RANK(ORDER BY col) OVER (...)
33552                // Oracle allows: RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
33553                let (order_by, args) = if self.check(TokenType::RParen) {
33554                    // RANK() - no arguments
33555                    (None, Vec::new())
33556                } else if self.match_token(TokenType::Order) {
33557                    // DuckDB: RANK(ORDER BY col)
33558                    self.expect(TokenType::By)?;
33559                    (Some(self.parse_order_by()?.expressions), Vec::new())
33560                } else {
33561                    // Oracle hypothetical: RANK(val1, val2, ...)
33562                    let mut args = vec![self.parse_expression()?];
33563                    while self.match_token(TokenType::Comma) {
33564                        args.push(self.parse_expression()?);
33565                    }
33566                    (None, args)
33567                };
33568                self.expect(TokenType::RParen)?;
33569                Ok(Expression::Rank(Rank { order_by, args }))
33570            }
33571            "DENSE_RANK" => {
33572                // Oracle allows: DENSE_RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
33573                let args = if self.check(TokenType::RParen) {
33574                    Vec::new()
33575                } else {
33576                    let mut args = vec![self.parse_expression()?];
33577                    while self.match_token(TokenType::Comma) {
33578                        args.push(self.parse_expression()?);
33579                    }
33580                    args
33581                };
33582                self.expect(TokenType::RParen)?;
33583                Ok(Expression::DenseRank(DenseRank { args }))
33584            }
33585            "PERCENT_RANK" => {
33586                // DuckDB allows: PERCENT_RANK(ORDER BY col) OVER (...)
33587                // Oracle allows: PERCENT_RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
33588                let (order_by, args) = if self.check(TokenType::RParen) {
33589                    // PERCENT_RANK() - no arguments
33590                    (None, Vec::new())
33591                } else if self.match_token(TokenType::Order) {
33592                    // DuckDB: PERCENT_RANK(ORDER BY col)
33593                    self.expect(TokenType::By)?;
33594                    (Some(self.parse_order_by()?.expressions), Vec::new())
33595                } else {
33596                    // Oracle hypothetical: PERCENT_RANK(val1, val2, ...)
33597                    let mut args = vec![self.parse_expression()?];
33598                    while self.match_token(TokenType::Comma) {
33599                        args.push(self.parse_expression()?);
33600                    }
33601                    (None, args)
33602                };
33603                self.expect(TokenType::RParen)?;
33604                Ok(Expression::PercentRank(PercentRank { order_by, args }))
33605            }
33606            "CUME_DIST" => {
33607                // DuckDB allows: CUME_DIST(ORDER BY col) OVER (...)
33608                // Oracle allows: CUME_DIST(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
33609                let (order_by, args) = if self.check(TokenType::RParen) {
33610                    // CUME_DIST() - no arguments
33611                    (None, Vec::new())
33612                } else if self.match_token(TokenType::Order) {
33613                    // DuckDB: CUME_DIST(ORDER BY col)
33614                    self.expect(TokenType::By)?;
33615                    (Some(self.parse_order_by()?.expressions), Vec::new())
33616                } else {
33617                    // Oracle hypothetical: CUME_DIST(val1, val2, ...)
33618                    let mut args = vec![self.parse_expression()?];
33619                    while self.match_token(TokenType::Comma) {
33620                        args.push(self.parse_expression()?);
33621                    }
33622                    (None, args)
33623                };
33624                self.expect(TokenType::RParen)?;
33625                Ok(Expression::CumeDist(CumeDist { order_by, args }))
33626            }
33627
33628            // NTILE
33629            "NTILE" => {
33630                // num_buckets is optional (Databricks allows NTILE() with no args)
33631                let num_buckets = if self.check(TokenType::RParen) {
33632                    None
33633                } else {
33634                    Some(self.parse_expression()?)
33635                };
33636
33637                // ClickHouse: NTILE can have extra args (e.g., ntile(3, 2)) — skip them
33638                while matches!(
33639                    self.config.dialect,
33640                    Some(crate::dialects::DialectType::ClickHouse)
33641                ) && self.match_token(TokenType::Comma)
33642                {
33643                    let _ = self.parse_expression()?;
33644                }
33645
33646                // DuckDB allows: NTILE(n ORDER BY col) OVER (...)
33647                let order_by = if self.match_token(TokenType::Order) {
33648                    self.expect(TokenType::By)?;
33649                    Some(self.parse_order_by()?.expressions)
33650                } else {
33651                    None
33652                };
33653                self.expect(TokenType::RParen)?;
33654                Ok(Expression::NTile(Box::new(NTileFunc {
33655                    num_buckets,
33656                    order_by,
33657                })))
33658            }
33659
33660            // LEAD / LAG
33661            "LEAD" | "LAG" => {
33662                let this = self.parse_expression()?;
33663                let (offset, default) = if self.match_token(TokenType::Comma) {
33664                    let off = self.parse_expression()?;
33665                    let def = if self.match_token(TokenType::Comma) {
33666                        Some(self.parse_expression()?)
33667                    } else {
33668                        None
33669                    };
33670                    (Some(off), def)
33671                } else {
33672                    (None, None)
33673                };
33674                // Check for IGNORE NULLS / RESPECT NULLS inside parens (e.g., Redshift: LAG(x IGNORE NULLS))
33675                let ignore_nulls_inside = if self.match_token(TokenType::Ignore)
33676                    && self.match_token(TokenType::Nulls)
33677                {
33678                    Some(true)
33679                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
33680                {
33681                    Some(false)
33682                } else {
33683                    None
33684                };
33685                self.expect(TokenType::RParen)?;
33686                // Also check for IGNORE NULLS / RESPECT NULLS after parens
33687                let ignore_nulls = if ignore_nulls_inside.is_some() {
33688                    ignore_nulls_inside
33689                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33690                    Some(true)
33691                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33692                    Some(false)
33693                } else {
33694                    None
33695                };
33696                let func = LeadLagFunc {
33697                    this,
33698                    offset,
33699                    default,
33700                    ignore_nulls,
33701                };
33702                Ok(if upper_name == "LEAD" {
33703                    Expression::Lead(Box::new(func))
33704                } else {
33705                    Expression::Lag(Box::new(func))
33706                })
33707            }
33708
33709            // FIRST_VALUE / LAST_VALUE
33710            "FIRST_VALUE" | "LAST_VALUE" => {
33711                let this = self.parse_expression()?;
33712                // Parse ORDER BY inside parens (e.g., DuckDB: LAST_VALUE(x ORDER BY x))
33713                let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33714                    self.parse_order_by_list()?
33715                } else {
33716                    Vec::new()
33717                };
33718                // Check for IGNORE NULLS / RESPECT NULLS inside the parens
33719                let mut ignore_nulls_inside = if self.match_token(TokenType::Ignore)
33720                    && self.match_token(TokenType::Nulls)
33721                {
33722                    Some(true)
33723                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
33724                {
33725                    Some(false) // RESPECT NULLS explicitly sets to false
33726                } else {
33727                    None
33728                };
33729                // Spark/Hive: first_value(col, true) means FIRST_VALUE(col) IGNORE NULLS
33730                if ignore_nulls_inside.is_none() && self.match_token(TokenType::Comma) {
33731                    let second_arg = self.parse_expression()?;
33732                    if let Expression::Boolean(BooleanLiteral { value: true }) = &second_arg {
33733                        ignore_nulls_inside = Some(true);
33734                    }
33735                    // If second arg is not true, just ignore it (not standard)
33736                }
33737                self.expect(TokenType::RParen)?;
33738                // Also check for IGNORE NULLS / RESPECT NULLS after the parens (some dialects use this syntax)
33739                let ignore_nulls: Option<bool> = if ignore_nulls_inside.is_some() {
33740                    ignore_nulls_inside
33741                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33742                    Some(true)
33743                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33744                    Some(false)
33745                } else {
33746                    None
33747                };
33748                let func = ValueFunc {
33749                    this,
33750                    ignore_nulls,
33751                    order_by,
33752                };
33753                Ok(if upper_name == "FIRST_VALUE" {
33754                    Expression::FirstValue(Box::new(func))
33755                } else {
33756                    Expression::LastValue(Box::new(func))
33757                })
33758            }
33759
33760            // NTH_VALUE
33761            "NTH_VALUE" => {
33762                let this = self.parse_expression()?;
33763                self.expect(TokenType::Comma)?;
33764                let offset = self.parse_expression()?;
33765                // Check for IGNORE NULLS / RESPECT NULLS inside the parens
33766                let ignore_nulls_inside = if self.match_token(TokenType::Ignore)
33767                    && self.match_token(TokenType::Nulls)
33768                {
33769                    Some(true)
33770                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
33771                {
33772                    Some(false)
33773                } else {
33774                    None
33775                };
33776                self.expect(TokenType::RParen)?;
33777                // Check for Snowflake FROM FIRST / FROM LAST after the parens
33778                let from_first = if self.match_keywords(&[TokenType::From, TokenType::First]) {
33779                    Some(true)
33780                } else if self.match_keywords(&[TokenType::From, TokenType::Last]) {
33781                    Some(false)
33782                } else {
33783                    None
33784                };
33785                // Also check for IGNORE NULLS / RESPECT NULLS after the parens (and after FROM FIRST/LAST)
33786                let ignore_nulls: Option<bool> = if ignore_nulls_inside.is_some() {
33787                    ignore_nulls_inside
33788                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33789                    Some(true)
33790                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33791                    Some(false)
33792                } else {
33793                    None
33794                };
33795                Ok(Expression::NthValue(Box::new(NthValueFunc {
33796                    this,
33797                    offset,
33798                    ignore_nulls,
33799                    from_first,
33800                })))
33801            }
33802            _ => unreachable!(
33803                "phase-6 window parser called with non-window family name '{}'",
33804                canonical_upper_name
33805            ),
33806        }
33807    }
33808
33809    fn parse_typed_json_family(
33810        &mut self,
33811        name: &str,
33812        upper_name: &str,
33813        canonical_upper_name: &str,
33814    ) -> Result<Expression> {
33815        match canonical_upper_name {
33816            // JSON functions
33817            "JSON_EXTRACT" | "JSON_EXTRACT_SCALAR" | "JSON_QUERY" | "JSON_VALUE" => {
33818                let this = self.parse_expression()?;
33819                // Path is optional for some dialects (e.g., TSQL JSON_QUERY with 1 arg defaults to '$')
33820                let path = if self.match_token(TokenType::Comma) {
33821                    self.parse_expression()?
33822                } else {
33823                    // Default path is '$' when not provided
33824                    Expression::Literal(Box::new(Literal::String("$".to_string())))
33825                };
33826
33827                // SQLite JSON_EXTRACT supports multiple paths - check for additional paths
33828                // If multiple paths, use generic Function instead of typed expression
33829                if self.check(TokenType::Comma)
33830                    && !self.check_identifier("WITH")
33831                    && !self.check_identifier("WITHOUT")
33832                    && !self.check_identifier("KEEP")
33833                    && !self.check_identifier("OMIT")
33834                    && !self.check_identifier("NULL")
33835                    && !self.check_identifier("ERROR")
33836                    && !self.check_identifier("EMPTY")
33837                    && !self.check(TokenType::Returning)
33838                {
33839                    let mut args = vec![this, path];
33840                    while self.match_token(TokenType::Comma) {
33841                        args.push(self.parse_expression()?);
33842                    }
33843                    self.expect(TokenType::RParen)?;
33844                    let func_expr = Expression::Function(Box::new(Function {
33845                        name: name.to_string(),
33846                        args,
33847                        distinct: false,
33848                        trailing_comments: Vec::new(),
33849                        use_bracket_syntax: false,
33850                        no_parens: false,
33851                        quoted: false,
33852                        span: None,
33853                        inferred_type: None,
33854                    }));
33855                    // Exasol: JSON_EXTRACT(...) EMITS (col1 TYPE1, col2 TYPE2)
33856                    if matches!(
33857                        self.config.dialect,
33858                        Some(crate::dialects::DialectType::Exasol)
33859                    ) && self.check_identifier("EMITS")
33860                    {
33861                        self.skip(); // consume EMITS
33862                        if let Some(schema) = self.parse_schema()? {
33863                            return Ok(Expression::FunctionEmits(Box::new(FunctionEmits {
33864                                this: func_expr,
33865                                emits: schema,
33866                            })));
33867                        }
33868                    }
33869                    return Ok(func_expr);
33870                }
33871
33872                // Parse JSON_QUERY/JSON_VALUE options (Trino/Presto style)
33873                // Options: WITH/WITHOUT [CONDITIONAL|UNCONDITIONAL] [ARRAY] WRAPPER
33874                //          KEEP QUOTES / OMIT QUOTES [ON SCALAR STRING]
33875                //          NULL ON ERROR / ERROR ON ERROR / EMPTY ON ERROR
33876                //          RETURNING type
33877                let mut wrapper_option: Option<String> = None;
33878                let mut quotes_option: Option<String> = None;
33879                let mut on_scalar_string = false;
33880                let mut on_error: Option<String> = None;
33881                let mut returning: Option<DataType> = None;
33882
33883                // Keep parsing options until we see RParen
33884                while !self.check(TokenType::RParen) {
33885                    // WITH [CONDITIONAL|UNCONDITIONAL] [ARRAY] WRAPPER - match in order of specificity
33886                    if self.match_text_seq(&["WITH", "UNCONDITIONAL", "ARRAY", "WRAPPER"]) {
33887                        wrapper_option = Some("WITH UNCONDITIONAL ARRAY WRAPPER".to_string());
33888                    } else if self.match_text_seq(&["WITH", "CONDITIONAL", "ARRAY", "WRAPPER"]) {
33889                        wrapper_option = Some("WITH CONDITIONAL ARRAY WRAPPER".to_string());
33890                    } else if self.match_text_seq(&["WITH", "UNCONDITIONAL", "WRAPPER"]) {
33891                        wrapper_option = Some("WITH UNCONDITIONAL WRAPPER".to_string());
33892                    } else if self.match_text_seq(&["WITH", "CONDITIONAL", "WRAPPER"]) {
33893                        wrapper_option = Some("WITH CONDITIONAL WRAPPER".to_string());
33894                    } else if self.match_text_seq(&["WITH", "ARRAY", "WRAPPER"]) {
33895                        wrapper_option = Some("WITH ARRAY WRAPPER".to_string());
33896                    } else if self.match_text_seq(&["WITH", "WRAPPER"]) {
33897                        wrapper_option = Some("WITH WRAPPER".to_string());
33898                    // WITHOUT [CONDITIONAL] [ARRAY] WRAPPER
33899                    } else if self.match_text_seq(&["WITHOUT", "CONDITIONAL", "ARRAY", "WRAPPER"]) {
33900                        wrapper_option = Some("WITHOUT CONDITIONAL ARRAY WRAPPER".to_string());
33901                    } else if self.match_text_seq(&["WITHOUT", "CONDITIONAL", "WRAPPER"]) {
33902                        wrapper_option = Some("WITHOUT CONDITIONAL WRAPPER".to_string());
33903                    } else if self.match_text_seq(&["WITHOUT", "ARRAY", "WRAPPER"]) {
33904                        wrapper_option = Some("WITHOUT ARRAY WRAPPER".to_string());
33905                    } else if self.match_text_seq(&["WITHOUT", "WRAPPER"]) {
33906                        wrapper_option = Some("WITHOUT WRAPPER".to_string());
33907                    } else if self.match_text_seq(&["KEEP", "QUOTES"]) {
33908                        // KEEP QUOTES
33909                        quotes_option = Some("KEEP QUOTES".to_string());
33910                    } else if self.match_text_seq(&["OMIT", "QUOTES", "ON", "SCALAR", "STRING"]) {
33911                        // OMIT QUOTES ON SCALAR STRING
33912                        quotes_option = Some("OMIT QUOTES".to_string());
33913                        on_scalar_string = true;
33914                    } else if self.match_text_seq(&["OMIT", "QUOTES"]) {
33915                        // OMIT QUOTES
33916                        quotes_option = Some("OMIT QUOTES".to_string());
33917                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
33918                        on_error = Some("NULL ON ERROR".to_string());
33919                    } else if self.match_text_seq(&["ERROR", "ON", "ERROR"]) {
33920                        on_error = Some("ERROR ON ERROR".to_string());
33921                    } else if self.match_text_seq(&["EMPTY", "ON", "ERROR"]) {
33922                        on_error = Some("EMPTY ON ERROR".to_string());
33923                    } else if self.match_token(TokenType::Returning) {
33924                        // RETURNING type
33925                        returning = Some(self.parse_data_type()?);
33926                    } else {
33927                        // No more options recognized, break
33928                        break;
33929                    }
33930                }
33931
33932                self.expect(TokenType::RParen)?;
33933                let func = JsonExtractFunc {
33934                    this,
33935                    path,
33936                    returning,
33937                    arrow_syntax: false,
33938                    hash_arrow_syntax: false,
33939                    wrapper_option,
33940                    quotes_option,
33941                    on_scalar_string,
33942                    on_error,
33943                };
33944                Ok(match upper_name {
33945                    "JSON_EXTRACT" => Expression::JsonExtract(Box::new(func)),
33946                    "JSON_EXTRACT_SCALAR" => Expression::JsonExtractScalar(Box::new(func)),
33947                    "JSON_QUERY" => Expression::JsonQuery(Box::new(func)),
33948                    "JSON_VALUE" => Expression::JsonValue(Box::new(func)),
33949                    _ => unreachable!("JSON function name already matched in caller"),
33950                })
33951            }
33952            // JSON_KEYS, TO_JSON, PARSE_JSON etc. support additional args including named args (BigQuery)
33953            // e.g., JSON_KEYS(expr, depth, mode => 'lax'), TO_JSON(expr, stringify_wide_numbers => FALSE)
33954            // e.g., PARSE_JSON('{}', wide_number_mode => 'exact')
33955            "JSON_ARRAY_LENGTH" | "JSON_KEYS" | "JSON_TYPE" | "TO_JSON" | "PARSE_JSON" => {
33956                let this = self.parse_expression()?;
33957                // ClickHouse: expr AS alias inside function args
33958                let this = self.maybe_clickhouse_alias(this);
33959
33960                // Check for additional arguments (comma-separated, possibly named)
33961                if self.match_token(TokenType::Comma) {
33962                    // Has additional arguments - parse as generic Function to preserve all args
33963                    let mut all_args = vec![this];
33964                    let remaining = self.parse_function_arguments()?;
33965                    all_args.extend(remaining);
33966                    self.expect(TokenType::RParen)?;
33967                    Ok(Expression::Function(Box::new(Function {
33968                        name: name.to_string(),
33969                        args: all_args,
33970                        distinct: false,
33971                        trailing_comments: Vec::new(),
33972                        use_bracket_syntax: false,
33973                        no_parens: false,
33974                        quoted: false,
33975                        span: None,
33976                        inferred_type: None,
33977                    })))
33978                } else {
33979                    // Single argument - use typed expression
33980                    self.expect(TokenType::RParen)?;
33981                    let func = UnaryFunc::new(this);
33982                    Ok(match canonical_upper_name {
33983                        "JSON_ARRAY_LENGTH" => Expression::JsonArrayLength(Box::new(func)),
33984                        "JSON_KEYS" => Expression::JsonKeys(Box::new(func)),
33985                        "JSON_TYPE" => Expression::JsonType(Box::new(func)),
33986                        "TO_JSON" => Expression::ToJson(Box::new(func)),
33987                        "PARSE_JSON" => Expression::ParseJson(Box::new(func)),
33988                        _ => unreachable!("JSON function name already matched in caller"),
33989                    })
33990                }
33991            }
33992
33993            // JSON_OBJECT with SQL standard syntax: JSON_OBJECT('key': value, ...) or JSON_OBJECT(*)
33994            "JSON_OBJECT" => {
33995                let mut pairs = Vec::new();
33996                let mut star = false;
33997                if !self.check(TokenType::RParen) {
33998                    // Check for JSON_OBJECT(*) syntax
33999                    if self.check(TokenType::Star) && self.check_next(TokenType::RParen) {
34000                        self.skip(); // consume *
34001                        star = true;
34002                    } else {
34003                        loop {
34004                            // Check for KEY keyword for KEY 'key' IS value syntax (KEY is a keyword token)
34005                            let has_key_keyword = self.match_token(TokenType::Key);
34006                            // Parse key: try string first (for 'key' syntax), then column
34007                            let key = if let Some(s) = self.parse_string()? {
34008                                s
34009                            } else {
34010                                // Use parse_column for key to avoid interpreting colon as JSON path
34011                                self.parse_column()?.ok_or_else(|| {
34012                                    self.parse_error("Expected key expression in JSON_OBJECT")
34013                                })?
34014                            };
34015
34016                            // Support colon, VALUE keyword (identifier), and IS keyword (for KEY...IS syntax)
34017                            let has_separator = self.match_token(TokenType::Colon)
34018                                || self.match_identifier("VALUE")
34019                                || (has_key_keyword && self.match_token(TokenType::Is));
34020
34021                            if has_separator {
34022                                let value = self.parse_bitwise()?.ok_or_else(|| {
34023                                    self.parse_error("Expected value expression in JSON_OBJECT")
34024                                })?;
34025                                // Check for FORMAT JSON after value
34026                                let value_with_format = if self.match_text_seq(&["FORMAT", "JSON"])
34027                                {
34028                                    Expression::JSONFormat(Box::new(JSONFormat {
34029                                        this: Some(Box::new(value)),
34030                                        options: Vec::new(),
34031                                        is_json: None,
34032                                        to_json: None,
34033                                    }))
34034                                } else {
34035                                    value
34036                                };
34037                                pairs.push((key, value_with_format));
34038                            } else {
34039                                // Just key/value pairs without separator
34040                                if self.match_token(TokenType::Comma) {
34041                                    let value = self.parse_bitwise()?.ok_or_else(|| {
34042                                        self.parse_error("Expected value expression in JSON_OBJECT")
34043                                    })?;
34044                                    pairs.push((key, value));
34045                                } else {
34046                                    return Err(self
34047                                        .parse_error("Expected value expression in JSON_OBJECT"));
34048                                }
34049                            }
34050                            if !self.match_token(TokenType::Comma) {
34051                                break;
34052                            }
34053                        }
34054                    }
34055                }
34056                // Parse optional modifiers: NULL ON NULL, ABSENT ON NULL, WITH UNIQUE KEYS
34057                let null_handling = if self.match_token(TokenType::Null) {
34058                    self.match_token(TokenType::On);
34059                    self.match_token(TokenType::Null);
34060                    Some(JsonNullHandling::NullOnNull)
34061                } else if self.match_identifier("ABSENT") {
34062                    self.match_token(TokenType::On);
34063                    self.match_token(TokenType::Null);
34064                    Some(JsonNullHandling::AbsentOnNull)
34065                } else {
34066                    None
34067                };
34068                let with_unique_keys = if self.match_token(TokenType::With) {
34069                    self.match_token(TokenType::Unique);
34070                    self.match_identifier("KEYS");
34071                    true
34072                } else {
34073                    false
34074                };
34075                // Parse optional RETURNING clause: RETURNING type [FORMAT JSON] [ENCODING encoding]
34076                let (returning_type, format_json, encoding) = if self
34077                    .match_token(TokenType::Returning)
34078                {
34079                    let return_type = self.parse_data_type()?;
34080                    // Optional FORMAT JSON
34081                    let has_format_json = if self.match_token(TokenType::Format) {
34082                        // JSON might be a keyword or identifier
34083                        let _ = self.match_token(TokenType::Json) || self.match_identifier("JSON");
34084                        true
34085                    } else {
34086                        false
34087                    };
34088                    // Optional ENCODING encoding
34089                    let enc = if self.match_identifier("ENCODING") {
34090                        Some(self.expect_identifier_or_keyword()?)
34091                    } else {
34092                        None
34093                    };
34094                    (Some(return_type), has_format_json, enc)
34095                } else {
34096                    (None, false, None)
34097                };
34098                self.expect(TokenType::RParen)?;
34099                Ok(Expression::JsonObject(Box::new(JsonObjectFunc {
34100                    pairs,
34101                    null_handling,
34102                    with_unique_keys,
34103                    returning_type,
34104                    format_json,
34105                    encoding,
34106                    star,
34107                })))
34108            }
34109
34110            // JSON_ARRAY function with Oracle-specific options
34111            // JSON_ARRAY(expr [FORMAT JSON], ... [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
34112            "JSON_ARRAY" => {
34113                let mut expressions = Vec::new();
34114                if !self.check(TokenType::RParen) {
34115                    loop {
34116                        let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
34117                        // Check for FORMAT JSON after each expression
34118                        let expr_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
34119                            Expression::JSONFormat(Box::new(JSONFormat {
34120                                this: Some(Box::new(expr)),
34121                                options: Vec::new(),
34122                                is_json: None,
34123                                to_json: None,
34124                            }))
34125                        } else {
34126                            expr
34127                        };
34128                        expressions.push(expr_with_format);
34129                        if !self.match_token(TokenType::Comma) {
34130                            break;
34131                        }
34132                    }
34133                }
34134                // Parse NULL ON NULL or ABSENT ON NULL
34135                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
34136                    Some(Box::new(Expression::Var(Box::new(Var {
34137                        this: "NULL ON NULL".to_string(),
34138                    }))))
34139                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
34140                    Some(Box::new(Expression::Var(Box::new(Var {
34141                        this: "ABSENT ON NULL".to_string(),
34142                    }))))
34143                } else {
34144                    None
34145                };
34146                // Parse RETURNING type
34147                let return_type = if self.match_token(TokenType::Returning) {
34148                    let dt = self.parse_data_type()?;
34149                    Some(Box::new(Expression::DataType(dt)))
34150                } else {
34151                    None
34152                };
34153                // Parse STRICT
34154                let strict = if self.match_identifier("STRICT") {
34155                    Some(Box::new(Expression::Boolean(BooleanLiteral {
34156                        value: true,
34157                    })))
34158                } else {
34159                    None
34160                };
34161                self.expect(TokenType::RParen)?;
34162                Ok(Expression::JSONArray(Box::new(JSONArray {
34163                    expressions,
34164                    null_handling,
34165                    return_type,
34166                    strict,
34167                })))
34168            }
34169
34170            // JSON_ARRAYAGG function with Oracle-specific options
34171            // JSON_ARRAYAGG(expr [FORMAT JSON] [ORDER BY ...] [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
34172            "JSON_ARRAYAGG" => {
34173                let this = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
34174                // Check for FORMAT JSON after the expression
34175                let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
34176                    Expression::JSONFormat(Box::new(JSONFormat {
34177                        this: Some(Box::new(this)),
34178                        options: Vec::new(),
34179                        is_json: None,
34180                        to_json: None,
34181                    }))
34182                } else {
34183                    this
34184                };
34185                // Parse ORDER BY clause
34186                let order = if self.match_token(TokenType::Order) {
34187                    self.match_token(TokenType::By);
34188                    // Parse comma-separated ordered expressions
34189                    let mut order_exprs = Vec::new();
34190                    loop {
34191                        if let Some(ordered) = self.parse_ordered_item()? {
34192                            order_exprs.push(ordered);
34193                        } else {
34194                            break;
34195                        }
34196                        if !self.match_token(TokenType::Comma) {
34197                            break;
34198                        }
34199                    }
34200                    if !order_exprs.is_empty() {
34201                        Some(Box::new(Expression::OrderBy(Box::new(OrderBy {
34202                            expressions: order_exprs,
34203                            siblings: false,
34204                            comments: Vec::new(),
34205                        }))))
34206                    } else {
34207                        None
34208                    }
34209                } else {
34210                    None
34211                };
34212                // Parse NULL ON NULL or ABSENT ON NULL
34213                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
34214                    Some(Box::new(Expression::Var(Box::new(Var {
34215                        this: "NULL ON NULL".to_string(),
34216                    }))))
34217                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
34218                    Some(Box::new(Expression::Var(Box::new(Var {
34219                        this: "ABSENT ON NULL".to_string(),
34220                    }))))
34221                } else {
34222                    None
34223                };
34224                // Parse RETURNING type
34225                let return_type = if self.match_token(TokenType::Returning) {
34226                    let dt = self.parse_data_type()?;
34227                    Some(Box::new(Expression::DataType(dt)))
34228                } else {
34229                    None
34230                };
34231                // Parse STRICT
34232                let strict = if self.match_identifier("STRICT") {
34233                    Some(Box::new(Expression::Boolean(BooleanLiteral {
34234                        value: true,
34235                    })))
34236                } else {
34237                    None
34238                };
34239                self.expect(TokenType::RParen)?;
34240                Ok(Expression::JSONArrayAgg(Box::new(JSONArrayAgg {
34241                    this: Box::new(this_with_format),
34242                    order,
34243                    null_handling,
34244                    return_type,
34245                    strict,
34246                })))
34247            }
34248
34249            // JSON_OBJECTAGG with KEY...VALUE syntax
34250            // JSON_OBJECTAGG(KEY key VALUE value) or JSON_OBJECTAGG(key: value)
34251            "JSON_OBJECTAGG" => {
34252                // Check for KEY keyword (KEY is a keyword token, not an identifier)
34253                let _has_key_keyword = self.match_token(TokenType::Key);
34254                // Parse key: use column parsing to avoid colon being interpreted as JSON path
34255                let key = self.parse_column()?.unwrap_or(Expression::Null(Null));
34256
34257                // Support colon, comma (MySQL), or VALUE keyword
34258                let _ = self.match_token(TokenType::Colon)
34259                    || self.match_token(TokenType::Comma)
34260                    || self.match_identifier("VALUE");
34261
34262                let value = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
34263                // Check for FORMAT JSON after value
34264                let value_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
34265                    Expression::JSONFormat(Box::new(JSONFormat {
34266                        this: Some(Box::new(value)),
34267                        options: Vec::new(),
34268                        is_json: None,
34269                        to_json: None,
34270                    }))
34271                } else {
34272                    value
34273                };
34274                // Parse NULL ON NULL or ABSENT ON NULL
34275                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
34276                    Some(Box::new(Expression::Var(Box::new(Var {
34277                        this: "NULL ON NULL".to_string(),
34278                    }))))
34279                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
34280                    Some(Box::new(Expression::Var(Box::new(Var {
34281                        this: "ABSENT ON NULL".to_string(),
34282                    }))))
34283                } else {
34284                    None
34285                };
34286                // Parse WITH/WITHOUT UNIQUE KEYS
34287                let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE"]) {
34288                    self.match_identifier("KEYS");
34289                    Some(Box::new(Expression::Boolean(BooleanLiteral {
34290                        value: true,
34291                    })))
34292                } else if self.match_text_seq(&["WITHOUT", "UNIQUE"]) {
34293                    self.match_identifier("KEYS");
34294                    Some(Box::new(Expression::Boolean(BooleanLiteral {
34295                        value: false,
34296                    })))
34297                } else {
34298                    None
34299                };
34300                // Parse RETURNING type
34301                let return_type = if self.match_token(TokenType::Returning) {
34302                    let dt = self.parse_data_type()?;
34303                    Some(Box::new(Expression::DataType(dt)))
34304                } else {
34305                    None
34306                };
34307                self.expect(TokenType::RParen)?;
34308                Ok(Expression::JSONObjectAgg(Box::new(JSONObjectAgg {
34309                    expressions: vec![Expression::JSONKeyValue(Box::new(JSONKeyValue {
34310                        this: Box::new(key),
34311                        expression: Box::new(value_with_format),
34312                    }))],
34313                    null_handling,
34314                    unique_keys,
34315                    return_type,
34316                    encoding: None,
34317                })))
34318            }
34319
34320            // JSON_TABLE function - MySQL/Oracle table function for JSON data
34321            // JSON_TABLE(json_doc [FORMAT JSON], path COLUMNS (column_list)) [AS alias]
34322            "JSON_TABLE" => {
34323                // Parse the JSON expression
34324                let this = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
34325                // Check for FORMAT JSON after the expression
34326                let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
34327                    Expression::JSONFormat(Box::new(JSONFormat {
34328                        this: Some(Box::new(this)),
34329                        options: Vec::new(),
34330                        is_json: None,
34331                        to_json: None,
34332                    }))
34333                } else {
34334                    this
34335                };
34336
34337                // Parse path (after comma)
34338                let path = if self.match_token(TokenType::Comma) {
34339                    if let Some(s) = self.parse_string()? {
34340                        Some(Box::new(s))
34341                    } else {
34342                        None
34343                    }
34344                } else {
34345                    None
34346                };
34347
34348                // Oracle uses "ERROR ON ERROR" (value then behavior) instead of "ON ERROR ERROR"
34349                // Parse error handling: ERROR ON ERROR or NULL ON ERROR
34350                let error_handling =
34351                    if self.match_identifier("ERROR") && self.match_text_seq(&["ON", "ERROR"]) {
34352                        Some(Box::new(Expression::Var(Box::new(Var {
34353                            this: "ERROR ON ERROR".to_string(),
34354                        }))))
34355                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
34356                        Some(Box::new(Expression::Var(Box::new(Var {
34357                            this: "NULL ON ERROR".to_string(),
34358                        }))))
34359                    } else {
34360                        None
34361                    };
34362
34363                // Parse empty handling: ERROR ON EMPTY or NULL ON EMPTY
34364                let empty_handling =
34365                    if self.match_identifier("ERROR") && self.match_text_seq(&["ON", "EMPTY"]) {
34366                        Some(Box::new(Expression::Var(Box::new(Var {
34367                            this: "ERROR ON EMPTY".to_string(),
34368                        }))))
34369                    } else if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
34370                        Some(Box::new(Expression::Var(Box::new(Var {
34371                            this: "NULL ON EMPTY".to_string(),
34372                        }))))
34373                    } else {
34374                        None
34375                    };
34376
34377                // Parse COLUMNS clause
34378                let schema = self.parse_json_table_columns()?;
34379
34380                self.expect(TokenType::RParen)?;
34381
34382                Ok(Expression::JSONTable(Box::new(JSONTable {
34383                    this: Box::new(this_with_format),
34384                    schema: schema.map(Box::new),
34385                    path,
34386                    error_handling,
34387                    empty_handling,
34388                })))
34389            }
34390            _ => unreachable!(
34391                "phase-6 json parser called with non-json family name '{}'",
34392                canonical_upper_name
34393            ),
34394        }
34395    }
34396
34397    fn parse_typed_translate_teradata_family(
34398        &mut self,
34399        name: &str,
34400        _upper_name: &str,
34401        canonical_upper_name: &str,
34402    ) -> Result<Expression> {
34403        match canonical_upper_name {
34404            // Teradata: TRANSLATE(x USING charset [WITH ERROR])
34405            "TRANSLATE"
34406                if matches!(
34407                    self.config.dialect,
34408                    Some(crate::dialects::DialectType::Teradata)
34409                ) =>
34410            {
34411                let this = self.parse_expression()?;
34412                if self.match_token(TokenType::Using) {
34413                    let expression = self.parse_expression()?;
34414                    let with_error = if self.match_text_seq(&["WITH", "ERROR"]) {
34415                        Some(Box::new(Expression::Boolean(BooleanLiteral {
34416                            value: true,
34417                        })))
34418                    } else {
34419                        None
34420                    };
34421                    self.expect(TokenType::RParen)?;
34422                    Ok(Expression::TranslateCharacters(Box::new(
34423                        TranslateCharacters {
34424                            this: Box::new(this),
34425                            expression: Box::new(expression),
34426                            with_error,
34427                        },
34428                    )))
34429                } else {
34430                    let mut args = vec![this];
34431                    if self.match_token(TokenType::Comma) {
34432                        let mut rest = self.parse_expression_list()?;
34433                        args.append(&mut rest);
34434                    }
34435                    self.expect(TokenType::RParen)?;
34436                    Ok(Expression::Function(Box::new(Function {
34437                        name: name.to_string(),
34438                        args,
34439                        distinct: false,
34440                        trailing_comments: Vec::new(),
34441                        use_bracket_syntax: false,
34442                        no_parens: false,
34443                        quoted: false,
34444                        span: None,
34445                        inferred_type: None,
34446                    })))
34447                }
34448            }
34449
34450            _ => unreachable!(
34451                "phase-6 translate parser called with non-translate family name '{}'",
34452                canonical_upper_name
34453            ),
34454        }
34455    }
34456
34457    /// Parse a generic function call (fallback for unrecognized functions)
34458    fn parse_generic_function(&mut self, name: &str, quoted: bool) -> Result<Expression> {
34459        let is_known_agg = Self::is_aggregate_function(name);
34460
34461        let (args, distinct) = if self.check(TokenType::RParen) {
34462            (Vec::new(), false)
34463        } else if self.check(TokenType::Star) {
34464            // Check for DuckDB *COLUMNS(...) syntax first
34465            if self.check_next_identifier("COLUMNS")
34466                && self
34467                    .tokens
34468                    .get(self.current + 2)
34469                    .map(|t| t.token_type == TokenType::LParen)
34470                    .unwrap_or(false)
34471            {
34472                // Parse *COLUMNS(...) as a function argument
34473                (self.parse_function_arguments()?, false)
34474            } else {
34475                // Regular star: parse star modifiers like EXCLUDE/EXCEPT/REPLACE/RENAME
34476                // e.g., COLUMNS(* EXCLUDE (empid, dept))
34477                self.skip(); // consume *
34478                let star = self.parse_star_modifiers(None)?;
34479                let mut args = vec![Expression::Star(star)];
34480                // ClickHouse: func(*, col1, col2) — star followed by more args
34481                if self.match_token(TokenType::Comma) {
34482                    let rest = self.parse_function_arguments()?;
34483                    args.extend(rest);
34484                }
34485                (args, false)
34486            }
34487        } else if self.check(TokenType::Distinct)
34488            && !self.check_next(TokenType::Comma)
34489            && !self.check_next(TokenType::RParen)
34490        {
34491            // DISTINCT as aggregate modifier: func(DISTINCT expr)
34492            // Not when followed by comma or rparen — then DISTINCT is used as an identifier value
34493            self.skip(); // consume DISTINCT
34494            (self.parse_function_arguments()?, true)
34495        } else if is_known_agg && self.match_token(TokenType::All) {
34496            // ALL is the default quantifier, just consume it
34497            (self.parse_function_arguments()?, false)
34498        } else {
34499            (self.parse_function_arguments()?, false)
34500        };
34501
34502        // For known aggregate functions, check for IGNORE NULLS, ORDER BY, LIMIT inside parens
34503        let (ignore_nulls, order_by, agg_limit) = if is_known_agg {
34504            let ignore_nulls = if self.match_token(TokenType::Ignore)
34505                && self.match_token(TokenType::Nulls)
34506            {
34507                Some(true)
34508            } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls) {
34509                Some(false)
34510            } else {
34511                None
34512            };
34513
34514            let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
34515                self.parse_order_by_list()?
34516            } else {
34517                Vec::new()
34518            };
34519            let limit = if self.match_token(TokenType::Limit) {
34520                Some(Box::new(self.parse_expression()?))
34521            } else {
34522                None
34523            };
34524            (ignore_nulls, order_by, limit)
34525        } else {
34526            (None, Vec::new(), None)
34527        };
34528
34529        // ClickHouse: SETTINGS key=value, ... before closing paren in function calls
34530        if matches!(
34531            self.config.dialect,
34532            Some(crate::dialects::DialectType::ClickHouse)
34533        ) && self.check(TokenType::Settings)
34534            && self.current + 2 < self.tokens.len()
34535            && (self.tokens[self.current + 1].token_type == TokenType::Var
34536                || self.tokens[self.current + 1].token_type == TokenType::Identifier)
34537            && self.tokens[self.current + 2].token_type == TokenType::Eq
34538        {
34539            self.skip(); // consume SETTINGS
34540            loop {
34541                let _key = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34542                    self.advance().text
34543                } else {
34544                    break;
34545                };
34546                if self.match_token(TokenType::Eq) {
34547                    let _value = self.parse_primary()?;
34548                }
34549                if !self.match_token(TokenType::Comma) {
34550                    break;
34551                }
34552            }
34553        }
34554
34555        self.expect(TokenType::RParen)?;
34556        let trailing_comments = self.previous_trailing_comments().to_vec();
34557
34558        // Check for WITHIN GROUP (ORDER BY ...)
34559        if self.match_identifier("WITHIN") {
34560            if self.match_identifier("GROUP") {
34561                self.expect(TokenType::LParen)?;
34562                self.expect(TokenType::Order)?;
34563                self.expect(TokenType::By)?;
34564                let within_order = self.parse_order_by_list()?;
34565                self.expect(TokenType::RParen)?;
34566
34567                let func_expr = Expression::AggregateFunction(Box::new(AggregateFunction {
34568                    name: name.to_string(),
34569                    args,
34570                    distinct,
34571                    filter: None,
34572                    order_by: Vec::new(),
34573                    limit: None,
34574                    ignore_nulls: None,
34575                    inferred_type: None,
34576                }));
34577
34578                let within = Expression::WithinGroup(Box::new(WithinGroup {
34579                    this: func_expr,
34580                    order_by: within_order,
34581                }));
34582
34583                // Check for FILTER after WITHIN GROUP
34584                let filter = self.parse_filter_clause()?;
34585                if let Some(filter_expr) = filter {
34586                    return Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
34587                        name: format!("__WITHIN_GROUP_{}", name),
34588                        args: vec![within, filter_expr],
34589                        distinct: false,
34590                        filter: None,
34591                        order_by: Vec::new(),
34592                        limit: None,
34593                        ignore_nulls: None,
34594                        inferred_type: None,
34595                    })));
34596                }
34597
34598                return Ok(within);
34599            }
34600        }
34601
34602        let filter = self.parse_filter_clause()?;
34603
34604        // Check for postfix IGNORE NULLS / RESPECT NULLS after RParen
34605        let ignore_nulls = if ignore_nulls.is_some() {
34606            ignore_nulls
34607        } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
34608            Some(true)
34609        } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
34610            Some(false)
34611        } else {
34612            None
34613        };
34614
34615        if filter.is_some() || is_known_agg || ignore_nulls.is_some() {
34616            Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
34617                name: name.to_string(),
34618                args,
34619                distinct,
34620                filter,
34621                order_by,
34622                limit: agg_limit,
34623                ignore_nulls,
34624                inferred_type: None,
34625            })))
34626        } else {
34627            let mut func = Function::new(name.to_string(), args);
34628            func.distinct = distinct;
34629            func.trailing_comments = trailing_comments;
34630            func.quoted = quoted;
34631            Ok(Expression::Function(Box::new(func)))
34632        }
34633    }
34634
34635    /// Check for an AS alias after an expression in ClickHouse function arg context.
34636    fn maybe_clickhouse_alias(&mut self, expr: Expression) -> Expression {
34637        if matches!(
34638            self.config.dialect,
34639            Some(crate::dialects::DialectType::ClickHouse)
34640        ) && self.check(TokenType::As)
34641            && !self.check_next(TokenType::RParen)
34642            && !self.check_next(TokenType::Comma)
34643        {
34644            let next_idx = self.current + 1;
34645            let is_alias = next_idx < self.tokens.len()
34646                && matches!(
34647                    self.tokens[next_idx].token_type,
34648                    TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
34649                );
34650            if is_alias {
34651                self.skip(); // consume AS
34652                let alias_token = self.advance();
34653                let alias_name = Identifier {
34654                    name: alias_token.text.clone(),
34655                    quoted: alias_token.token_type == TokenType::QuotedIdentifier,
34656                    trailing_comments: Vec::new(),
34657                    span: None,
34658                };
34659                return Expression::Alias(Box::new(crate::expressions::Alias {
34660                    this: expr,
34661                    alias: alias_name,
34662                    column_aliases: Vec::new(),
34663                    pre_alias_comments: Vec::new(),
34664                    trailing_comments: Vec::new(),
34665                    inferred_type: None,
34666                }));
34667            }
34668        }
34669        expr
34670    }
34671
34672    /// Parse an expression, then check for AS alias in ClickHouse function arg context.
34673    /// ClickHouse allows: func(expr AS alias, ...) where AS creates a named alias inside function args.
34674    fn parse_expression_with_clickhouse_alias(&mut self) -> Result<Expression> {
34675        let expr = self.parse_expression()?;
34676        Ok(self.maybe_clickhouse_alias(expr))
34677    }
34678
34679    /// Parse function arguments, handling named arguments (name => value, name := value)
34680    /// and TABLE/MODEL prefixed arguments (BigQuery)
34681    fn parse_function_arguments(&mut self) -> Result<Vec<Expression>> {
34682        let mut args = Vec::new();
34683
34684        loop {
34685            // ClickHouse: SETTINGS key=value, ... terminates function args
34686            // Only break if SETTINGS is followed by identifier = value pattern
34687            if matches!(
34688                self.config.dialect,
34689                Some(crate::dialects::DialectType::ClickHouse)
34690            ) && self.check(TokenType::Settings)
34691                && self.current + 2 < self.tokens.len()
34692                && (self.tokens[self.current + 1].token_type == TokenType::Var
34693                    || self.tokens[self.current + 1].token_type == TokenType::Identifier)
34694                && self.tokens[self.current + 2].token_type == TokenType::Eq
34695            {
34696                break; // will be consumed by SETTINGS handler after loop
34697            }
34698
34699            // ClickHouse: bare SELECT/WITH as function argument (e.g., view(SELECT 1), remote(..., view(SELECT ...)))
34700            if matches!(
34701                self.config.dialect,
34702                Some(crate::dialects::DialectType::ClickHouse)
34703            ) && (self.check(TokenType::Select) || self.check(TokenType::With))
34704            {
34705                let query = self.parse_statement()?;
34706                args.push(query);
34707                if !self.match_token(TokenType::Comma) {
34708                    break;
34709                }
34710                continue;
34711            }
34712
34713            // Check for TABLE ref or MODEL ref as function argument (BigQuery)
34714            // e.g., GAP_FILL(TABLE device_data, ...) or ML.PREDICT(MODEL mydataset.mymodel, ...)
34715            let is_table_or_model_arg = if !self.is_at_end() {
34716                self.check(TokenType::Table) || self.peek().text.eq_ignore_ascii_case("MODEL")
34717            } else {
34718                false
34719            };
34720            let arg = if is_table_or_model_arg {
34721                let prefix = self.peek().text.to_ascii_uppercase();
34722                let saved_pos = self.current;
34723                self.skip(); // consume TABLE or MODEL
34724
34725                // Only treat as TABLE/MODEL argument if followed by an identifier (table name),
34726                // not by => (which would be a named arg like "table => value")
34727                if !self.is_at_end()
34728                    && !self.check(TokenType::FArrow)
34729                    && !self.check(TokenType::ColonEq)
34730                {
34731                    // Parse the table/model reference (supports dotted names like dataset.table)
34732                    if let Some(table_expr) = self.parse_table_parts()? {
34733                        Expression::TableArgument(Box::new(TableArgument {
34734                            prefix,
34735                            this: table_expr,
34736                        }))
34737                    } else {
34738                        // Failed to parse table parts, backtrack and treat as regular expression
34739                        self.current = saved_pos;
34740                        self.parse_expression()?
34741                    }
34742                } else {
34743                    // TABLE/MODEL followed by => or :=, backtrack and handle as named arg
34744                    self.current = saved_pos;
34745                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34746                        let ident_token = self.advance();
34747                        let ident_name = ident_token.text.clone();
34748                        if self.match_token(TokenType::FArrow) {
34749                            let value = self.parse_expression()?;
34750                            Expression::NamedArgument(Box::new(NamedArgument {
34751                                name: Identifier::new(ident_name),
34752                                value,
34753                                separator: NamedArgSeparator::DArrow,
34754                            }))
34755                        } else if self.match_token(TokenType::ColonEq) {
34756                            let value = self.parse_expression()?;
34757                            Expression::NamedArgument(Box::new(NamedArgument {
34758                                name: Identifier::new(ident_name),
34759                                value,
34760                                separator: NamedArgSeparator::ColonEq,
34761                            }))
34762                        } else {
34763                            self.current = saved_pos;
34764                            self.parse_expression()?
34765                        }
34766                    } else {
34767                        self.parse_expression()?
34768                    }
34769                }
34770            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34771                // Try to parse:
34772                // 1. Named argument: identifier => value or identifier := value
34773                // 2. Snowflake lambda with type: identifier type -> body (e.g., a int -> a + 1)
34774                // Save position to backtrack if not a named argument
34775                let saved_pos = self.current;
34776
34777                // Try to get identifier
34778                let ident_token = self.advance();
34779                let ident_name = ident_token.text.clone();
34780
34781                // PostgreSQL/Redshift VARIADIC keyword: backtrack and let parse_expression handle it
34782                // VARIADIC ARRAY[...] must not be misinterpreted as a lambda with type annotation
34783                if ident_name.eq_ignore_ascii_case("VARIADIC")
34784                    && matches!(
34785                        self.config.dialect,
34786                        Some(crate::dialects::DialectType::PostgreSQL)
34787                            | Some(crate::dialects::DialectType::Redshift)
34788                    )
34789                {
34790                    self.current = saved_pos;
34791                    self.parse_expression()?
34792                }
34793                // Check for Snowflake lambda with type annotation: a int -> body
34794                // Look ahead to see if we have a type token followed by ->
34795                else if !self.is_at_end()
34796                    && self.is_type_keyword()
34797                    && !self.check(TokenType::FArrow)
34798                    && !self.check(TokenType::ColonEq)
34799                {
34800                    // Parse type annotation
34801                    let type_annotation = self.parse_data_type()?;
34802
34803                    // Check for arrow
34804                    if self.match_token(TokenType::Arrow) {
34805                        // This is a Snowflake lambda: param type -> body
34806                        let body = self.parse_expression()?;
34807                        Expression::Lambda(Box::new(LambdaExpr {
34808                            parameters: vec![Identifier::new(ident_name)],
34809                            body,
34810                            colon: false,
34811                            parameter_types: vec![Some(type_annotation)],
34812                        }))
34813                    } else {
34814                        // Not a lambda, backtrack and parse as regular expression
34815                        self.current = saved_pos;
34816                        self.parse_expression()?
34817                    }
34818                }
34819                // ClickHouse: simple lambda without type annotation: ident -> body
34820                else if self.match_token(TokenType::Arrow) {
34821                    let body = self.parse_expression()?;
34822                    Expression::Lambda(Box::new(LambdaExpr {
34823                        parameters: vec![Identifier::new(ident_name)],
34824                        body,
34825                        colon: false,
34826                        parameter_types: Vec::new(),
34827                    }))
34828                }
34829                // Check for named argument separator (=> is FArrow)
34830                else if self.match_token(TokenType::FArrow) {
34831                    // name => value
34832                    let value = self.parse_expression()?;
34833                    Expression::NamedArgument(Box::new(NamedArgument {
34834                        name: Identifier::new(ident_name),
34835                        value,
34836                        separator: NamedArgSeparator::DArrow,
34837                    }))
34838                } else if self.match_token(TokenType::ColonEq) {
34839                    // name := value
34840                    let value = self.parse_expression()?;
34841                    Expression::NamedArgument(Box::new(NamedArgument {
34842                        name: Identifier::new(ident_name),
34843                        value,
34844                        separator: NamedArgSeparator::ColonEq,
34845                    }))
34846                } else {
34847                    // Not a named argument, backtrack and parse as regular expression
34848                    self.current = saved_pos;
34849                    self.parse_expression()?
34850                }
34851            } else {
34852                // Regular expression
34853                self.parse_expression()?
34854            };
34855
34856            // Handle AS alias inside function arguments (e.g. ClickHouse: arrayJoin([1,2,3] AS src))
34857            let arg = if matches!(
34858                self.config.dialect,
34859                Some(crate::dialects::DialectType::ClickHouse)
34860            ) && self.check(TokenType::As)
34861                && !self.check_next(TokenType::RParen)
34862                && !self.check_next(TokenType::Comma)
34863            {
34864                // Look ahead: AS followed by identifier/keyword, then ) or , means it's an alias
34865                let next_idx = self.current + 1;
34866                let after_alias_idx = self.current + 2;
34867                let is_alias_token = next_idx < self.tokens.len()
34868                    && (matches!(
34869                        self.tokens[next_idx].token_type,
34870                        TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
34871                    ) || self.tokens[next_idx].token_type.is_keyword());
34872                // Ensure the token AFTER the alias is ) or , (function arg boundary)
34873                let is_alias = is_alias_token
34874                    && after_alias_idx < self.tokens.len()
34875                    && matches!(
34876                        self.tokens[after_alias_idx].token_type,
34877                        TokenType::RParen | TokenType::Comma
34878                    );
34879                if is_alias {
34880                    self.skip(); // consume AS
34881                    let alias_token = self.advance();
34882                    let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
34883                        let mut ident = Identifier::new(alias_token.text.clone());
34884                        ident.quoted = true;
34885                        ident
34886                    } else {
34887                        Identifier::new(alias_token.text.clone())
34888                    };
34889                    Expression::Alias(Box::new(crate::expressions::Alias {
34890                        this: arg,
34891                        alias: alias_name,
34892                        column_aliases: Vec::new(),
34893                        pre_alias_comments: Vec::new(),
34894                        trailing_comments: Vec::new(),
34895                        inferred_type: None,
34896                    }))
34897                } else {
34898                    arg
34899                }
34900            } else {
34901                arg
34902            };
34903
34904            // ClickHouse: implicit alias without AS keyword: func(expr identifier, ...)
34905            let arg = self.try_clickhouse_implicit_alias(arg);
34906
34907            // Handle trailing comments
34908            let trailing_comments = self.previous_trailing_comments().to_vec();
34909            let arg = if trailing_comments.is_empty() {
34910                arg
34911            } else {
34912                match &arg {
34913                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
34914                        Expression::Annotated(Box::new(Annotated {
34915                            this: arg,
34916                            trailing_comments,
34917                        }))
34918                    }
34919                    _ => arg,
34920                }
34921            };
34922
34923            args.push(arg);
34924
34925            if !self.match_token(TokenType::Comma) {
34926                break;
34927            }
34928            // Skip consecutive commas (Snowflake allows skipping optional named args)
34929            // e.g., ROUND(SCALE => 1, EXPR => 2.25, , ROUNDING_MODE => 'HALF_TO_EVEN')
34930            while self.check(TokenType::Comma) {
34931                self.skip();
34932            }
34933        }
34934
34935        // ClickHouse: SETTINGS key=value, ... at end of function args before RParen
34936        if matches!(
34937            self.config.dialect,
34938            Some(crate::dialects::DialectType::ClickHouse)
34939        ) && self.check(TokenType::Settings)
34940            && self.current + 2 < self.tokens.len()
34941            && (self.tokens[self.current + 1].token_type == TokenType::Var
34942                || self.tokens[self.current + 1].token_type == TokenType::Identifier)
34943            && self.tokens[self.current + 2].token_type == TokenType::Eq
34944        {
34945            self.skip(); // consume SETTINGS
34946            loop {
34947                let _key = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34948                    self.advance().text
34949                } else {
34950                    break;
34951                };
34952                if self.match_token(TokenType::Eq) {
34953                    let _value = self.parse_primary()?;
34954                }
34955                if !self.match_token(TokenType::Comma) {
34956                    break;
34957                }
34958            }
34959        }
34960
34961        Ok(args)
34962    }
34963
34964    /// Parse optional FILTER clause
34965    fn parse_filter_clause(&mut self) -> Result<Option<Expression>> {
34966        if self.match_token(TokenType::Filter) {
34967            self.expect(TokenType::LParen)?;
34968            // WHERE is optional (DuckDB allows FILTER(condition) without WHERE)
34969            self.match_token(TokenType::Where);
34970            let filter_expr = self.parse_expression()?;
34971            self.expect(TokenType::RParen)?;
34972            Ok(Some(filter_expr))
34973        } else {
34974            Ok(None)
34975        }
34976    }
34977
34978    /// Parse STRUCT arguments with optional AS aliases: STRUCT(x, y AS name, ...)
34979    fn parse_struct_args(&mut self) -> Result<Vec<Expression>> {
34980        let mut args = Vec::new();
34981
34982        loop {
34983            let expr = self.parse_expression()?;
34984
34985            // Check for AS alias
34986            if self.match_token(TokenType::As) {
34987                let alias = self.expect_identifier_or_keyword()?;
34988                args.push(Expression::Alias(Box::new(Alias {
34989                    this: expr,
34990                    alias: Identifier::new(alias),
34991                    column_aliases: Vec::new(),
34992                    pre_alias_comments: Vec::new(),
34993                    trailing_comments: Vec::new(),
34994                    inferred_type: None,
34995                })));
34996            } else {
34997                args.push(expr);
34998            }
34999
35000            if !self.match_token(TokenType::Comma) {
35001                break;
35002            }
35003        }
35004
35005        Ok(args)
35006    }
35007
35008    /// Maybe parse OVER clause for window functions or WITHIN GROUP for ordered-set aggregates
35009    fn maybe_parse_over(&mut self, expr: Expression) -> Result<Expression> {
35010        let expr = self.maybe_parse_subscript(expr)?;
35011
35012        // For Oracle: Check for interval span after expression (e.g., (expr) DAY(9) TO SECOND(3))
35013        // https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Interval-Expressions.html
35014        let expr = if matches!(
35015            self.config.dialect,
35016            Some(crate::dialects::DialectType::Oracle)
35017        ) {
35018            self.try_parse_oracle_interval_span(expr)?
35019        } else {
35020            expr
35021        };
35022
35023        // Check for WITHIN GROUP (for ordered-set aggregate functions like LISTAGG, PERCENTILE_CONT)
35024        let expr = if self.check(TokenType::Within) && self.check_next(TokenType::Group) {
35025            self.skip(); // consume WITHIN
35026            self.skip(); // consume GROUP
35027            self.expect(TokenType::LParen)?;
35028            self.expect(TokenType::Order)?;
35029            self.expect(TokenType::By)?;
35030            let order_by = self.parse_order_by_list()?;
35031            self.expect(TokenType::RParen)?;
35032            Expression::WithinGroup(Box::new(WithinGroup {
35033                this: expr,
35034                order_by,
35035            }))
35036        } else {
35037            expr
35038        };
35039
35040        // Check for FILTER clause (can follow WITHIN GROUP or standalone aggregate)
35041        // SQL:2003 syntax: aggregate_function(...) FILTER (WHERE condition)
35042        let expr = if self.match_token(TokenType::Filter) {
35043            self.expect(TokenType::LParen)?;
35044            // WHERE is required in standard SQL FILTER clause
35045            self.expect(TokenType::Where)?;
35046            let filter_expr = self.parse_expression()?;
35047            self.expect(TokenType::RParen)?;
35048            Expression::Filter(Box::new(Filter {
35049                this: Box::new(expr),
35050                expression: Box::new(filter_expr),
35051            }))
35052        } else {
35053            expr
35054        };
35055
35056        // ClickHouse: IGNORE NULLS / RESPECT NULLS modifier after function call (before OVER)
35057        // This handles cases like: func(args) IGNORE NULLS OVER w
35058        // and parametric aggregates: func(params)(args) IGNORE NULLS
35059        let expr = if matches!(
35060            self.config.dialect,
35061            Some(crate::dialects::DialectType::ClickHouse)
35062        ) && (self.match_keywords(&[TokenType::Ignore, TokenType::Nulls])
35063            || self.match_keywords(&[TokenType::Respect, TokenType::Nulls]))
35064        {
35065            // Consume the modifier — we don't need to store it for transpilation
35066            expr
35067        } else {
35068            expr
35069        };
35070
35071        // Check for KEEP clause (Oracle: aggregate KEEP (DENSE_RANK FIRST|LAST ORDER BY ...))
35072        // Only if KEEP is followed by LPAREN - otherwise KEEP is used as an alias
35073        let keep = if self.check(TokenType::Keep) && self.check_next(TokenType::LParen) {
35074            self.skip(); // consume KEEP
35075            Some(self.parse_keep_clause()?)
35076        } else {
35077            None
35078        };
35079
35080        // Check for OVER clause (can follow KEEP, FILTER, WITHIN GROUP, or standalone aggregate)
35081        if self.match_token(TokenType::Over) {
35082            let over = self.parse_over_clause()?;
35083            Ok(Expression::WindowFunction(Box::new(WindowFunction {
35084                this: expr,
35085                over,
35086                keep,
35087                inferred_type: None,
35088            })))
35089        } else if keep.is_some() {
35090            // KEEP without OVER - still a window-like construct
35091            // Create a WindowFunction with empty Over
35092            Ok(Expression::WindowFunction(Box::new(WindowFunction {
35093                this: expr,
35094                over: Over {
35095                    window_name: None,
35096                    partition_by: Vec::new(),
35097                    order_by: Vec::new(),
35098                    frame: None,
35099                    alias: None,
35100                },
35101                keep,
35102                inferred_type: None,
35103            })))
35104        } else {
35105            Ok(expr)
35106        }
35107    }
35108
35109    /// ClickHouse: parse parameterized aggregate functions like func(params)(args)
35110    fn maybe_parse_clickhouse_parameterized_agg(&mut self, expr: Expression) -> Result<Expression> {
35111        if !matches!(
35112            self.config.dialect,
35113            Some(crate::dialects::DialectType::ClickHouse)
35114        ) {
35115            return Ok(expr);
35116        }
35117        if !self.check(TokenType::LParen) {
35118            return Ok(expr);
35119        }
35120
35121        let (name, quoted, params) = match expr {
35122            Expression::Function(func) => (func.name, func.quoted, func.args),
35123            Expression::AggregateFunction(agg) => {
35124                if agg.distinct
35125                    || agg.filter.is_some()
35126                    || !agg.order_by.is_empty()
35127                    || agg.limit.is_some()
35128                    || agg.ignore_nulls.is_some()
35129                {
35130                    return Ok(Expression::AggregateFunction(agg));
35131                }
35132                (agg.name, false, agg.args)
35133            }
35134            _ => return Ok(expr),
35135        };
35136
35137        self.skip(); // consume (
35138                     // Handle DISTINCT in second arg list: func(params)(DISTINCT args)
35139        let distinct = self.match_token(TokenType::Distinct);
35140        let expressions = if self.check(TokenType::RParen) {
35141            Vec::new()
35142        } else {
35143            self.parse_function_arguments()?
35144        };
35145        self.expect(TokenType::RParen)?;
35146
35147        let ident = Identifier {
35148            name,
35149            quoted,
35150            trailing_comments: Vec::new(),
35151            span: None,
35152        };
35153
35154        // If DISTINCT was used, wrap the result to indicate it
35155        // For now, we just include it in the CombinedParameterizedAgg
35156        let _ = distinct; // DISTINCT is consumed but not separately tracked in this AST node
35157        Ok(Expression::CombinedParameterizedAgg(Box::new(
35158            CombinedParameterizedAgg {
35159                this: Box::new(Expression::Identifier(ident)),
35160                params,
35161                expressions,
35162            },
35163        )))
35164    }
35165
35166    /// Parse Oracle KEEP clause: KEEP (DENSE_RANK FIRST|LAST ORDER BY ...)
35167    fn parse_keep_clause(&mut self) -> Result<Keep> {
35168        self.expect(TokenType::LParen)?;
35169
35170        // Expect DENSE_RANK
35171        if !self.match_identifier("DENSE_RANK") {
35172            return Err(self.parse_error("Expected DENSE_RANK in KEEP clause"));
35173        }
35174
35175        // Expect FIRST or LAST
35176        let first = if self.match_token(TokenType::First) {
35177            true
35178        } else if self.match_token(TokenType::Last) {
35179            false
35180        } else {
35181            return Err(self.parse_error("Expected FIRST or LAST in KEEP clause"));
35182        };
35183
35184        // Expect ORDER BY
35185        self.expect(TokenType::Order)?;
35186        self.expect(TokenType::By)?;
35187
35188        let order_by = self.parse_order_by_list()?;
35189
35190        self.expect(TokenType::RParen)?;
35191
35192        Ok(Keep { first, order_by })
35193    }
35194
35195    /// Parse a JSON path operand - just the immediate literal/identifier without any subscript processing
35196    /// This is used for JSON arrow operators (->, ->>) to get proper left-to-right associativity
35197    fn parse_json_path_operand(&mut self) -> Result<Expression> {
35198        // Negative number literal (e.g., -1)
35199        if self.check(TokenType::Dash) {
35200            let dash_pos = self.current;
35201            self.skip(); // consume the dash
35202            if self.check(TokenType::Number) {
35203                let token = self.advance();
35204                return Ok(Expression::Neg(Box::new(UnaryOp {
35205                    this: Expression::Literal(Box::new(Literal::Number(token.text))),
35206                    inferred_type: None,
35207                })));
35208            }
35209            // Not a negative number, backtrack
35210            self.current = dash_pos;
35211        }
35212
35213        // Number literal
35214        if self.check(TokenType::Number) {
35215            let token = self.advance();
35216            // Check for numeric literal suffix encoded as "number::TYPE" by tokenizer
35217            if let Some(sep_pos) = token.text.find("::") {
35218                let num_part = &token.text[..sep_pos];
35219                let type_name = &token.text[sep_pos + 2..];
35220                let num_expr = Expression::Literal(Box::new(Literal::Number(num_part.to_string())));
35221                let data_type = match type_name {
35222                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
35223                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
35224                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
35225                    "DOUBLE" => crate::expressions::DataType::Double {
35226                        precision: None,
35227                        scale: None,
35228                    },
35229                    "FLOAT" => crate::expressions::DataType::Float {
35230                        precision: None,
35231                        scale: None,
35232                        real_spelling: false,
35233                    },
35234                    "DECIMAL" => crate::expressions::DataType::Decimal {
35235                        precision: None,
35236                        scale: None,
35237                    },
35238                    _ => crate::expressions::DataType::Custom {
35239                        name: type_name.to_string(),
35240                    },
35241                };
35242                return Ok(Expression::TryCast(Box::new(crate::expressions::Cast {
35243                    this: num_expr,
35244                    to: data_type,
35245                    trailing_comments: Vec::new(),
35246                    double_colon_syntax: false,
35247                    format: None,
35248                    default: None,
35249                    inferred_type: None,
35250                })));
35251            }
35252            return Ok(Expression::Literal(Box::new(Literal::Number(token.text))));
35253        }
35254
35255        // String literal
35256        if self.check(TokenType::String) {
35257            let token = self.advance();
35258            return Ok(Expression::Literal(Box::new(Literal::String(token.text))));
35259        }
35260
35261        // Parenthesized expression (for complex paths)
35262        if self.match_token(TokenType::LParen) {
35263            let expr = self.parse_expression()?;
35264            self.expect(TokenType::RParen)?;
35265            return Ok(Expression::Paren(Box::new(Paren {
35266                this: expr,
35267                trailing_comments: Vec::new(),
35268            })));
35269        }
35270
35271        // Array literal: ['$.family', '$.species']
35272        // Used in DuckDB for multi-path JSON extraction
35273        if self.match_token(TokenType::LBracket) {
35274            // Empty array: []
35275            if self.match_token(TokenType::RBracket) {
35276                return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
35277                    expressions: Vec::new(),
35278                    bracket_notation: true,
35279                    use_list_keyword: false,
35280                })));
35281            }
35282
35283            // Parse array elements
35284            let mut expressions = vec![self.parse_expression()?];
35285            while self.match_token(TokenType::Comma) {
35286                if self.check(TokenType::RBracket) {
35287                    break;
35288                }
35289                expressions.push(self.parse_expression()?);
35290            }
35291            self.expect(TokenType::RBracket)?;
35292
35293            return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
35294                expressions,
35295                bracket_notation: true,
35296                use_list_keyword: false,
35297            })));
35298        }
35299
35300        // Identifier (possibly qualified like table.column)
35301        if self.is_identifier_token() {
35302            let first_ident = self.expect_identifier_with_quoted()?;
35303
35304            // Check for qualified name: identifier.identifier
35305            if self.match_token(TokenType::Dot) {
35306                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
35307                    let second_ident = if self.is_identifier_token() {
35308                        self.expect_identifier_with_quoted()?
35309                    } else {
35310                        let token = self.advance();
35311                        Identifier::new(token.text)
35312                    };
35313                    return Ok(Expression::boxed_column(Column {
35314                        name: second_ident,
35315                        table: Some(first_ident),
35316                        join_mark: false,
35317                        trailing_comments: Vec::new(),
35318                        span: None,
35319                        inferred_type: None,
35320                    }));
35321                }
35322            }
35323
35324            return Ok(Expression::boxed_column(Column {
35325                name: first_ident,
35326                table: None,
35327                join_mark: false,
35328                trailing_comments: Vec::new(),
35329                span: None,
35330                inferred_type: None,
35331            }));
35332        }
35333
35334        // Keywords as identifiers (possibly qualified)
35335        if self.is_safe_keyword_as_identifier() {
35336            let token = self.advance();
35337            let first_ident = Identifier::new(token.text);
35338
35339            // Check for qualified name: identifier.identifier
35340            if self.match_token(TokenType::Dot) {
35341                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
35342                    let second_ident = if self.is_identifier_token() {
35343                        self.expect_identifier_with_quoted()?
35344                    } else {
35345                        let token = self.advance();
35346                        Identifier::new(token.text)
35347                    };
35348                    return Ok(Expression::boxed_column(Column {
35349                        name: second_ident,
35350                        table: Some(first_ident),
35351                        join_mark: false,
35352                        trailing_comments: Vec::new(),
35353                        span: None,
35354                        inferred_type: None,
35355                    }));
35356                }
35357            }
35358
35359            return Ok(Expression::boxed_column(Column {
35360                name: first_ident,
35361                table: None,
35362                join_mark: false,
35363                trailing_comments: Vec::new(),
35364                span: None,
35365                inferred_type: None,
35366            }));
35367        }
35368
35369        Err(self.parse_error(format!(
35370            "Unexpected token in JSON path: {:?}",
35371            self.peek().token_type
35372        )))
35373    }
35374
35375    /// Maybe parse subscript access (array[index], struct.field)
35376    fn maybe_parse_subscript(&mut self, mut expr: Expression) -> Result<Expression> {
35377        loop {
35378            // ClickHouse: empty brackets [] in JSON paths represent Array(JSON) type access.
35379            // json.a.b[] -> json.a.b.:"Array(JSON)"
35380            // json.a.b[][] -> json.a.b.:"Array(Array(JSON))"
35381            // Check for consecutive empty bracket pairs before normal bracket handling.
35382            if matches!(
35383                self.config.dialect,
35384                Some(crate::dialects::DialectType::ClickHouse)
35385            ) && self.check(TokenType::LBracket)
35386            {
35387                let is_empty_bracket = self
35388                    .peek_nth(1)
35389                    .map_or(false, |t| t.token_type == TokenType::RBracket);
35390                if is_empty_bracket {
35391                    let mut bracket_json_type: Option<DataType> = None;
35392                    while self.check(TokenType::LBracket) {
35393                        let is_empty = self
35394                            .peek_nth(1)
35395                            .map_or(false, |t| t.token_type == TokenType::RBracket);
35396                        if is_empty {
35397                            self.skip(); // consume [
35398                            self.skip(); // consume ]
35399                            bracket_json_type = Some(DataType::Array {
35400                                element_type: Box::new(bracket_json_type.unwrap_or(DataType::Json)),
35401                                dimension: None,
35402                            });
35403                        } else {
35404                            break;
35405                        }
35406                    }
35407                    if let Some(json_type) = bracket_json_type {
35408                        expr = Expression::JSONCast(Box::new(crate::expressions::JSONCast {
35409                            this: Box::new(expr),
35410                            to: json_type,
35411                        }));
35412                        continue;
35413                    }
35414                }
35415            }
35416
35417            if self.match_token(TokenType::LBracket) {
35418                // Check if expr is an array/list constructor keyword (ARRAY[...] or LIST[...])
35419                let array_constructor_type = match &expr {
35420                    Expression::Column(col) if col.table.is_none() => {
35421                        let upper = col.name.name.to_ascii_uppercase();
35422                        if upper == "ARRAY" || upper == "LIST" {
35423                            Some(upper)
35424                        } else {
35425                            None
35426                        }
35427                    }
35428                    Expression::Identifier(id) => {
35429                        let upper = id.name.to_ascii_uppercase();
35430                        if upper == "ARRAY" || upper == "LIST" {
35431                            Some(upper)
35432                        } else {
35433                            None
35434                        }
35435                    }
35436                    _ => None,
35437                };
35438
35439                if let Some(constructor_type) = array_constructor_type {
35440                    // Parse ARRAY[expr, expr, ...] or LIST[expr, expr, ...]
35441                    // bracket_notation=false means we have the ARRAY/LIST keyword prefix
35442                    let use_list_keyword = constructor_type == "LIST";
35443                    if self.check(TokenType::RBracket) {
35444                        // Empty array: ARRAY[]
35445                        self.skip();
35446                        expr = Expression::ArrayFunc(Box::new(ArrayConstructor {
35447                            expressions: Vec::new(),
35448                            bracket_notation: false, // Has ARRAY/LIST keyword
35449                            use_list_keyword,
35450                        }));
35451                    } else {
35452                        let expressions = self.parse_expression_list()?;
35453                        self.expect(TokenType::RBracket)?;
35454                        expr = Expression::ArrayFunc(Box::new(ArrayConstructor {
35455                            expressions,
35456                            bracket_notation: false, // Has ARRAY/LIST keyword
35457                            use_list_keyword,
35458                        }));
35459                    }
35460                    continue;
35461                }
35462
35463                // Special case: MAP[...] constructor syntax
35464                // Check if expr is a MAP identifier
35465                // ClickHouse: map[key] is always subscript access, not a MAP constructor
35466                let is_map_constructor = !matches!(
35467                    self.config.dialect,
35468                    Some(crate::dialects::DialectType::ClickHouse)
35469                ) && match &expr {
35470                    Expression::Column(col) => {
35471                        col.name.name.eq_ignore_ascii_case("MAP") && col.table.is_none()
35472                    }
35473                    Expression::Identifier(id) => id.name.eq_ignore_ascii_case("MAP"),
35474                    _ => false,
35475                };
35476
35477                if is_map_constructor {
35478                    let is_materialize = matches!(
35479                        self.config.dialect,
35480                        Some(crate::dialects::DialectType::Materialize)
35481                    );
35482
35483                    // Materialize: MAP[] empty map or MAP['a' => 1, ...] with fat arrow
35484                    if is_materialize {
35485                        if self.check(TokenType::RBracket) {
35486                            // Empty map: MAP[]
35487                            self.skip();
35488                            expr = Expression::ToMap(Box::new(ToMap {
35489                                this: Box::new(Expression::Struct(Box::new(Struct {
35490                                    fields: Vec::new(),
35491                                }))),
35492                            }));
35493                            continue;
35494                        }
35495
35496                        // Parse MAP['a' => 1, 'b' => 2, ...] with fat arrow entries
35497                        // Store entries as PropertyEQ expressions (key => value)
35498                        let mut entries = Vec::new();
35499                        loop {
35500                            let key = self.parse_expression()?;
35501                            self.expect(TokenType::FArrow)?;
35502                            let value = self.parse_expression()?;
35503                            // Store as PropertyEQ which will be output as key => value
35504                            entries.push((
35505                                None,
35506                                Expression::PropertyEQ(Box::new(BinaryOp::new(key, value))),
35507                            ));
35508
35509                            if !self.match_token(TokenType::Comma) {
35510                                break;
35511                            }
35512                        }
35513                        self.expect(TokenType::RBracket)?;
35514
35515                        expr = Expression::ToMap(Box::new(ToMap {
35516                            this: Box::new(Expression::Struct(Box::new(Struct {
35517                                fields: entries,
35518                            }))),
35519                        }));
35520                        continue;
35521                    }
35522
35523                    // DuckDB/BigQuery: MAP[keys, values] syntax
35524                    let keys = self.parse_expression()?;
35525                    self.expect(TokenType::Comma)?;
35526                    let values = self.parse_expression()?;
35527                    self.expect(TokenType::RBracket)?;
35528                    expr = Expression::Function(Box::new(Function {
35529                        name: "MAP".to_string(),
35530                        args: vec![keys, values],
35531                        distinct: false,
35532                        trailing_comments: Vec::new(),
35533                        use_bracket_syntax: true,
35534                        no_parens: false,
35535                        quoted: false,
35536                        span: None,
35537                        inferred_type: None,
35538                    }));
35539                    continue;
35540                }
35541
35542                // Check for slice syntax: [start:end:step]
35543                // Handle [:...] case where start is omitted
35544                if self.check(TokenType::Colon) {
35545                    self.skip(); // consume first :
35546                                 // Parse end - use parse_slice_element to avoid : being interpreted as parameter
35547                    let end = self.parse_slice_element()?;
35548                    // Check for step (second colon)
35549                    let step = if self.match_token(TokenType::Colon) {
35550                        self.parse_slice_element()?
35551                    } else {
35552                        None
35553                    };
35554                    self.expect(TokenType::RBracket)?;
35555                    if step.is_some() {
35556                        // Three-part slice with step: Subscript with Slice index
35557                        let slice = Expression::Slice(Box::new(Slice {
35558                            this: None, // start is omitted
35559                            expression: end.map(Box::new),
35560                            step: step.map(Box::new),
35561                        }));
35562                        expr = Expression::Subscript(Box::new(Subscript {
35563                            this: expr,
35564                            index: slice,
35565                        }));
35566                    } else {
35567                        expr = Expression::ArraySlice(Box::new(ArraySlice {
35568                            this: expr,
35569                            start: None,
35570                            end,
35571                        }));
35572                    }
35573                } else {
35574                    let start = self.parse_slice_element()?;
35575                    // Check if this is a slice
35576                    if self.match_token(TokenType::Colon) {
35577                        let end = self.parse_slice_element()?;
35578                        // Check for step (second colon)
35579                        let step = if self.match_token(TokenType::Colon) {
35580                            self.parse_slice_element()?
35581                        } else {
35582                            None
35583                        };
35584                        self.expect(TokenType::RBracket)?;
35585                        if step.is_some() {
35586                            // Three-part slice with step: Subscript with Slice index
35587                            let slice = Expression::Slice(Box::new(Slice {
35588                                this: start.map(Box::new),
35589                                expression: end.map(Box::new),
35590                                step: step.map(Box::new),
35591                            }));
35592                            expr = Expression::Subscript(Box::new(Subscript {
35593                                this: expr,
35594                                index: slice,
35595                            }));
35596                        } else {
35597                            expr = Expression::ArraySlice(Box::new(ArraySlice {
35598                                this: expr,
35599                                start,
35600                                end,
35601                            }));
35602                        }
35603                    } else {
35604                        self.expect(TokenType::RBracket)?;
35605                        // Simple subscript access - start must be Some
35606                        let index =
35607                            start.unwrap_or_else(|| Expression::Null(crate::expressions::Null));
35608                        expr = Expression::Subscript(Box::new(Subscript { this: expr, index }));
35609                    }
35610                }
35611            } else if self.match_token(TokenType::DotColon) {
35612                // In ClickHouse, the type after .: may be a quoted identifier like "Array(JSON)"
35613                // which needs to be re-parsed as a proper data type.
35614                let data_type = if matches!(
35615                    self.config.dialect,
35616                    Some(crate::dialects::DialectType::ClickHouse)
35617                ) && self.check(TokenType::QuotedIdentifier)
35618                {
35619                    let type_text = self.advance().text.clone();
35620                    // Re-parse the quoted identifier text as a data type
35621                    self.parse_data_type_from_text(&type_text)?
35622                } else {
35623                    self.parse_data_type()?
35624                };
35625                expr = Expression::JSONCast(Box::new(JSONCast {
35626                    this: Box::new(expr),
35627                    to: data_type,
35628                }));
35629            } else if self.match_token(TokenType::Dot) {
35630                // Handle chained dot access (a.b.c.d)
35631                if self.match_token(TokenType::Star) {
35632                    // expr.* - struct field expansion with potential modifiers (EXCEPT, REPLACE, etc.)
35633                    let table_name = match &expr {
35634                        Expression::Column(col) => {
35635                            if let Some(ref table) = col.table {
35636                                Some(Identifier::new(format!("{}.{}", table.name, col.name.name)))
35637                            } else {
35638                                Some(col.name.clone())
35639                            }
35640                        }
35641                        Expression::Dot(d) => {
35642                            fn dot_to_name_inner(expr: &Expression) -> String {
35643                                match expr {
35644                                    Expression::Column(col) => {
35645                                        if let Some(ref table) = col.table {
35646                                            format!("{}.{}", table.name, col.name.name)
35647                                        } else {
35648                                            col.name.name.clone()
35649                                        }
35650                                    }
35651                                    Expression::Dot(d) => {
35652                                        format!("{}.{}", dot_to_name_inner(&d.this), d.field.name)
35653                                    }
35654                                    _ => String::new(),
35655                                }
35656                            }
35657                            Some(Identifier::new(dot_to_name_inner(&Expression::Dot(
35658                                d.clone(),
35659                            ))))
35660                        }
35661                        _ => None,
35662                    };
35663                    if table_name.is_some() {
35664                        let star = self.parse_star_modifiers(table_name)?;
35665                        expr = Expression::Star(star);
35666                        // ClickHouse: a.* APPLY(func) EXCEPT(col) REPLACE(expr AS col) in any order
35667                        if matches!(
35668                            self.config.dialect,
35669                            Some(crate::dialects::DialectType::ClickHouse)
35670                        ) {
35671                            loop {
35672                                if self.check(TokenType::Apply) {
35673                                    self.skip();
35674                                    let apply_expr = if self.match_token(TokenType::LParen) {
35675                                        let e = self.parse_expression()?;
35676                                        self.expect(TokenType::RParen)?;
35677                                        e
35678                                    } else {
35679                                        self.parse_expression()?
35680                                    };
35681                                    expr = Expression::Apply(Box::new(crate::expressions::Apply {
35682                                        this: Box::new(expr),
35683                                        expression: Box::new(apply_expr),
35684                                    }));
35685                                } else if self.check(TokenType::Except)
35686                                    || self.check(TokenType::Exclude)
35687                                {
35688                                    self.skip();
35689                                    self.match_identifier("STRICT");
35690                                    if self.match_token(TokenType::LParen) {
35691                                        loop {
35692                                            if self.check(TokenType::RParen) {
35693                                                break;
35694                                            }
35695                                            let _ = self.parse_expression()?;
35696                                            if !self.match_token(TokenType::Comma) {
35697                                                break;
35698                                            }
35699                                        }
35700                                        self.expect(TokenType::RParen)?;
35701                                    } else if self.is_identifier_token()
35702                                        || self.is_safe_keyword_as_identifier()
35703                                    {
35704                                        let _ = self.parse_expression()?;
35705                                    }
35706                                } else if self.check(TokenType::Replace) {
35707                                    self.skip();
35708                                    self.match_identifier("STRICT");
35709                                    if self.match_token(TokenType::LParen) {
35710                                        loop {
35711                                            if self.check(TokenType::RParen) {
35712                                                break;
35713                                            }
35714                                            let _ = self.parse_expression()?;
35715                                            if self.match_token(TokenType::As) {
35716                                                if self.is_identifier_token()
35717                                                    || self.is_safe_keyword_as_identifier()
35718                                                {
35719                                                    self.skip();
35720                                                }
35721                                            }
35722                                            if !self.match_token(TokenType::Comma) {
35723                                                break;
35724                                            }
35725                                        }
35726                                        self.expect(TokenType::RParen)?;
35727                                    } else {
35728                                        let _ = self.parse_expression()?;
35729                                        if self.match_token(TokenType::As) {
35730                                            if self.is_identifier_token()
35731                                                || self.is_safe_keyword_as_identifier()
35732                                            {
35733                                                self.skip();
35734                                            }
35735                                        }
35736                                    }
35737                                } else {
35738                                    break;
35739                                }
35740                            }
35741                        }
35742                    } else {
35743                        // For complex expressions (like CAST, function calls), use Dot with * as field
35744                        expr = Expression::Dot(Box::new(DotAccess {
35745                            this: expr,
35746                            field: Identifier::new("*"),
35747                        }));
35748                    }
35749                } else if self.check(TokenType::Identifier)
35750                    || self.check(TokenType::Var)
35751                    || self.check(TokenType::QuotedIdentifier)
35752                    || self.check_keyword()
35753                {
35754                    let is_quoted = self.check(TokenType::QuotedIdentifier);
35755                    let field_name = self.advance().text;
35756                    // Check if this is a method call (field followed by parentheses)
35757                    if self.check(TokenType::LParen) && !is_quoted {
35758                        // This is a method call like a.b.C() or x.EXTRACT()
35759                        self.skip(); // consume (
35760                        let args = if self.check(TokenType::RParen) {
35761                            Vec::new()
35762                        } else {
35763                            self.parse_expression_list()?
35764                        };
35765                        self.expect(TokenType::RParen)?;
35766                        // Create a method call expression (DotAccess with function call)
35767                        expr = Expression::MethodCall(Box::new(MethodCall {
35768                            this: expr,
35769                            method: Identifier::new(field_name),
35770                            args,
35771                        }));
35772                    } else {
35773                        let mut ident = Identifier::new(field_name);
35774                        if is_quoted {
35775                            ident.quoted = true;
35776                        }
35777                        expr = Expression::Dot(Box::new(DotAccess {
35778                            this: expr,
35779                            field: ident,
35780                        }));
35781                    }
35782                } else if self.check(TokenType::Number) {
35783                    // Handle numeric field access like a.0 or x.1
35784                    let field_name = self.advance().text;
35785                    expr = Expression::Dot(Box::new(DotAccess {
35786                        this: expr,
35787                        field: Identifier::new(field_name),
35788                    }));
35789                } else if matches!(
35790                    self.config.dialect,
35791                    Some(crate::dialects::DialectType::ClickHouse)
35792                ) && self.check(TokenType::Caret)
35793                {
35794                    // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
35795                    self.skip(); // consume ^
35796                                 // What follows should be an identifier path
35797                    let mut field_name = "^".to_string();
35798                    if self.check(TokenType::Identifier)
35799                        || self.check(TokenType::Var)
35800                        || self.check_keyword()
35801                    {
35802                        field_name.push_str(&self.advance().text);
35803                    }
35804                    expr = Expression::Dot(Box::new(DotAccess {
35805                        this: expr,
35806                        field: Identifier::new(field_name),
35807                    }));
35808                } else if matches!(
35809                    self.config.dialect,
35810                    Some(crate::dialects::DialectType::ClickHouse)
35811                ) && self.check(TokenType::Colon)
35812                {
35813                    // ClickHouse: json.path.:Type — the : prefix means type cast on JSON path
35814                    self.skip(); // consume :
35815                                 // Consume the type name
35816                    let mut type_name = ":".to_string();
35817                    if self.check(TokenType::Identifier)
35818                        || self.check(TokenType::Var)
35819                        || self.check_keyword()
35820                    {
35821                        type_name.push_str(&self.advance().text);
35822                    }
35823                    expr = Expression::Dot(Box::new(DotAccess {
35824                        this: expr,
35825                        field: Identifier::new(type_name),
35826                    }));
35827                } else if matches!(
35828                    self.config.dialect,
35829                    Some(crate::dialects::DialectType::ClickHouse)
35830                ) && self.check(TokenType::Dash)
35831                    && self
35832                        .peek_nth(1)
35833                        .is_some_and(|t| t.token_type == TokenType::Number)
35834                {
35835                    // ClickHouse: tuple.-1 — negative tuple index
35836                    self.skip(); // consume -
35837                    let num = self.advance().text;
35838                    expr = Expression::Dot(Box::new(DotAccess {
35839                        this: expr,
35840                        field: Identifier::new(format!("-{}", num)),
35841                    }));
35842                } else {
35843                    return Err(self.parse_error("Expected field name after dot"));
35844                }
35845            } else if self.match_token(TokenType::Collate) {
35846                // Parse COLLATE 'collation_name' or COLLATE "collation_name" or COLLATE collation_name
35847                let (collation, quoted, double_quoted) = if self.check(TokenType::String) {
35848                    // Single-quoted string: COLLATE 'de_DE'
35849                    (self.advance().text, true, false)
35850                } else if self.check(TokenType::QuotedIdentifier) {
35851                    // Double-quoted identifier: COLLATE "de_DE"
35852                    (self.advance().text, false, true)
35853                } else {
35854                    // Unquoted identifier: COLLATE de_DE
35855                    (self.expect_identifier_or_keyword()?, false, false)
35856                };
35857                expr = Expression::Collation(Box::new(CollationExpr {
35858                    this: expr,
35859                    collation,
35860                    quoted,
35861                    double_quoted,
35862                }));
35863            } else if self.check(TokenType::DColon)
35864                || self.check(TokenType::DColonDollar)
35865                || self.check(TokenType::DColonPercent)
35866                || self.check(TokenType::DColonQMark)
35867            {
35868                // For SingleStore, :: variants are JSON path extraction
35869                // For other dialects, :: is cast syntax (PostgreSQL-style)
35870                if matches!(
35871                    self.config.dialect,
35872                    Some(crate::dialects::DialectType::SingleStore)
35873                ) {
35874                    // SingleStore JSON path extraction: expr::key, expr::$key, expr::%key, expr::?key
35875                    if self.match_token(TokenType::DColon) {
35876                        // ::key -> JSON_EXTRACT_JSON(expr, 'key')
35877                        let path_key =
35878                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35879                                self.advance().text
35880                            } else if self.check(TokenType::Number) {
35881                                self.advance().text
35882                            } else if self.check(TokenType::QuotedIdentifier) {
35883                                self.advance().text
35884                            } else {
35885                                return Err(self.parse_error(
35886                                    "Expected identifier or number after :: in JSON path",
35887                                ));
35888                            };
35889                        expr = Expression::Function(Box::new(Function::new(
35890                            "JSON_EXTRACT_JSON".to_string(),
35891                            vec![expr, Expression::string(&path_key)],
35892                        )));
35893                    } else if self.match_token(TokenType::DColonDollar) {
35894                        // ::$key -> JSON_EXTRACT_STRING(expr, 'key')
35895                        let path_key =
35896                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35897                                self.advance().text
35898                            } else if self.check(TokenType::Number) {
35899                                self.advance().text
35900                            } else {
35901                                return Err(self.parse_error(
35902                                    "Expected identifier or number after ::$ in JSON path",
35903                                ));
35904                            };
35905                        expr = Expression::Function(Box::new(Function::new(
35906                            "JSON_EXTRACT_STRING".to_string(),
35907                            vec![expr, Expression::string(&path_key)],
35908                        )));
35909                    } else if self.match_token(TokenType::DColonPercent) {
35910                        // ::%key -> JSON_EXTRACT_DOUBLE(expr, 'key')
35911                        let path_key =
35912                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35913                                self.advance().text
35914                            } else if self.check(TokenType::Number) {
35915                                self.advance().text
35916                            } else {
35917                                return Err(self.parse_error(
35918                                    "Expected identifier or number after ::% in JSON path",
35919                                ));
35920                            };
35921                        expr = Expression::Function(Box::new(Function::new(
35922                            "JSON_EXTRACT_DOUBLE".to_string(),
35923                            vec![expr, Expression::string(&path_key)],
35924                        )));
35925                    } else if self.match_token(TokenType::DColonQMark) {
35926                        // ::?key -> SingleStoreJsonPathQMark function (for JSON_MATCH_ANY patterns)
35927                        let path_key =
35928                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35929                                self.advance().text
35930                            } else if self.check(TokenType::Number) {
35931                                self.advance().text
35932                            } else {
35933                                return Err(self.parse_error(
35934                                    "Expected identifier or number after ::? in JSON path",
35935                                ));
35936                            };
35937                        // Use a special function name that SingleStore generator will recognize
35938                        expr = Expression::Function(Box::new(Function::new(
35939                            "__SS_JSON_PATH_QMARK__".to_string(),
35940                            vec![expr, Expression::string(&path_key)],
35941                        )));
35942                    }
35943                } else {
35944                    // PostgreSQL :: cast operator: expr::type
35945                    self.skip(); // consume DColon
35946                                 // Use parse_data_type_for_cast to avoid consuming subscripts as array dimensions
35947                    let data_type = self.parse_data_type_for_cast()?;
35948                    expr = Expression::Cast(Box::new(Cast {
35949                        this: expr,
35950                        to: data_type,
35951                        trailing_comments: Vec::new(),
35952                        double_colon_syntax: true,
35953                        format: None,
35954                        default: None,
35955                        inferred_type: None,
35956                    }));
35957                }
35958            } else if self.match_token(TokenType::ColonGt) {
35959                // SingleStore :> cast operator: expr :> type
35960                let data_type = self.parse_data_type_for_cast()?;
35961                expr = Expression::Cast(Box::new(Cast {
35962                    this: expr,
35963                    to: data_type,
35964                    trailing_comments: Vec::new(),
35965                    double_colon_syntax: false, // Use :> syntax in generator
35966                    format: None,
35967                    default: None,
35968                    inferred_type: None,
35969                }));
35970            } else if self.match_token(TokenType::NColonGt) {
35971                // SingleStore !:> try cast operator: expr !:> type
35972                let data_type = self.parse_data_type_for_cast()?;
35973                expr = Expression::TryCast(Box::new(Cast {
35974                    this: expr,
35975                    to: data_type,
35976                    trailing_comments: Vec::new(),
35977                    double_colon_syntax: false,
35978                    format: None,
35979                    default: None,
35980                    inferred_type: None,
35981                }));
35982            } else if self.match_token(TokenType::QDColon) {
35983                // Databricks ?:: try cast operator: expr?::type
35984                let data_type = self.parse_data_type_for_cast()?;
35985                expr = Expression::TryCast(Box::new(Cast {
35986                    this: expr,
35987                    to: data_type,
35988                    trailing_comments: Vec::new(),
35989                    double_colon_syntax: true, // Uses :: style syntax
35990                    format: None,
35991                    default: None,
35992                    inferred_type: None,
35993                }));
35994            } else if self.check(TokenType::Arrow)
35995                && !matches!(
35996                    self.config.dialect,
35997                    Some(crate::dialects::DialectType::ClickHouse)
35998                )
35999            {
36000                self.skip(); // consume ->
36001                             // JSON extract operator: expr -> path (PostgreSQL, MySQL, DuckDB)
36002                             // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
36003                let path = self.parse_json_path_operand()?;
36004                expr = Expression::JsonExtract(Box::new(JsonExtractFunc {
36005                    this: expr,
36006                    path,
36007                    returning: None,
36008                    arrow_syntax: true,
36009                    hash_arrow_syntax: false,
36010                    wrapper_option: None,
36011                    quotes_option: None,
36012                    on_scalar_string: false,
36013                    on_error: None,
36014                }));
36015            } else if self.match_token(TokenType::DArrow) {
36016                // JSON extract text operator: expr ->> path (PostgreSQL, MySQL, DuckDB)
36017                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
36018                let path = self.parse_json_path_operand()?;
36019                expr = Expression::JsonExtractScalar(Box::new(JsonExtractFunc {
36020                    this: expr,
36021                    path,
36022                    returning: None,
36023                    arrow_syntax: true,
36024                    hash_arrow_syntax: false,
36025                    wrapper_option: None,
36026                    quotes_option: None,
36027                    on_scalar_string: false,
36028                    on_error: None,
36029                }));
36030            } else if self.match_token(TokenType::HashArrow) {
36031                // JSONB path extract: expr #> path (PostgreSQL)
36032                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
36033                let path = self.parse_json_path_operand()?;
36034                expr = Expression::JsonExtractPath(Box::new(JsonPathFunc {
36035                    this: expr,
36036                    paths: vec![path],
36037                }));
36038            } else if self.match_token(TokenType::DHashArrow) {
36039                // JSONB path extract text: expr #>> path (PostgreSQL)
36040                // For now, use JsonExtractScalar since the result is text
36041                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
36042                let path = self.parse_json_path_operand()?;
36043                expr = Expression::JsonExtractScalar(Box::new(JsonExtractFunc {
36044                    this: expr,
36045                    path,
36046                    returning: None,
36047                    arrow_syntax: false,     // This is #>> not ->>
36048                    hash_arrow_syntax: true, // Mark as #>> operator
36049                    wrapper_option: None,
36050                    quotes_option: None,
36051                    on_scalar_string: false,
36052                    on_error: None,
36053                }));
36054            } else if self.check_join_marker() {
36055                // Oracle/Redshift-style outer join marker: column (+)
36056                // Only applies to Column expressions
36057                if let Expression::Column(col) = &mut expr {
36058                    self.skip(); // consume (
36059                    self.skip(); // consume +
36060                    self.skip(); // consume )
36061                    col.join_mark = true;
36062                    // Don't continue - join marker is terminal (no more postfix ops after it)
36063                    break;
36064                }
36065                // If not a Column, just break - the marker is invalid in this context
36066                else {
36067                    break;
36068                }
36069            } else {
36070                break;
36071            }
36072        }
36073        Ok(expr)
36074    }
36075
36076    /// Check if the next tokens are the Oracle-style join marker (+)
36077    fn check_join_marker(&self) -> bool {
36078        self.check(TokenType::LParen)
36079            && self
36080                .peek_nth(1)
36081                .map_or(false, |t| t.token_type == TokenType::Plus)
36082            && self
36083                .peek_nth(2)
36084                .map_or(false, |t| t.token_type == TokenType::RParen)
36085    }
36086
36087    /// Parse OVER clause
36088    fn parse_over_clause(&mut self) -> Result<Over> {
36089        // Handle OVER window_name (without parentheses)
36090        if !self.check(TokenType::LParen) {
36091            // OVER window_name - just a named window reference
36092            let window_name = self.expect_identifier_or_keyword()?;
36093            return Ok(Over {
36094                window_name: Some(Identifier::new(window_name)),
36095                partition_by: Vec::new(),
36096                order_by: Vec::new(),
36097                frame: None,
36098                alias: None,
36099            });
36100        }
36101
36102        self.expect(TokenType::LParen)?;
36103
36104        // Check for named window reference at start of OVER clause
36105        // e.g., OVER (w ORDER BY y) - w is a window name that can be extended
36106        let window_name = if (self.check(TokenType::Identifier)
36107            || self.check(TokenType::Var)
36108            || self.check_keyword())
36109            && !self.check(TokenType::Partition)
36110            && !self.check(TokenType::Order)
36111            && !self.check(TokenType::Rows)
36112            && !self.check(TokenType::Range)
36113            && !self.check(TokenType::Groups)
36114            && !self.check(TokenType::Distribute)
36115            && !self.check(TokenType::Sort)
36116        {
36117            // Look ahead to see if next token indicates this is a window name
36118            let pos = self.current;
36119            let name = self.advance().text;
36120            // If next token is a keyword that can follow a window name, this is a named reference
36121            if self.check(TokenType::Order)
36122                || self.check(TokenType::Partition)
36123                || self.check(TokenType::Rows)
36124                || self.check(TokenType::Range)
36125                || self.check(TokenType::Groups)
36126                || self.check(TokenType::RParen)
36127                || self.check(TokenType::Distribute)
36128                || self.check(TokenType::Sort)
36129            {
36130                Some(Identifier::new(name))
36131            } else {
36132                // Not a named window, restore position
36133                self.current = pos;
36134                None
36135            }
36136        } else {
36137            None
36138        };
36139
36140        // Parse PARTITION BY or DISTRIBUTE BY (Hive uses DISTRIBUTE BY in window specs)
36141        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
36142            self.parse_expression_list()?
36143        } else if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
36144            // Hive: DISTRIBUTE BY is equivalent to PARTITION BY in window specs
36145            self.parse_expression_list()?
36146        } else {
36147            Vec::new()
36148        };
36149
36150        // Parse ORDER BY or SORT BY (Hive uses SORT BY in window specs)
36151        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By])
36152            || self.match_keywords(&[TokenType::Sort, TokenType::By])
36153        {
36154            let mut exprs = Vec::new();
36155            loop {
36156                let expr = self.parse_expression()?;
36157                let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
36158                    (true, false)
36159                } else if self.match_token(TokenType::Asc) {
36160                    (false, true)
36161                } else {
36162                    (false, false)
36163                };
36164                // ClickHouse/SQL: COLLATE 'collation' in window ORDER BY
36165                if self.match_token(TokenType::Collate) {
36166                    // Consume collation name (string or identifier)
36167                    if self.check(TokenType::String) {
36168                        self.skip();
36169                    } else if self.check(TokenType::QuotedIdentifier) {
36170                        self.skip();
36171                    } else {
36172                        let _ = self.expect_identifier_or_keyword();
36173                    }
36174                }
36175                let nulls_first = if self.match_token(TokenType::Nulls) {
36176                    if self.match_token(TokenType::First) {
36177                        Some(true)
36178                    } else if self.match_token(TokenType::Last) {
36179                        Some(false)
36180                    } else {
36181                        return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
36182                    }
36183                } else {
36184                    None
36185                };
36186                // ClickHouse: WITH FILL in window ORDER BY
36187                let with_fill = if matches!(
36188                    self.config.dialect,
36189                    Some(crate::dialects::DialectType::ClickHouse)
36190                ) && self.check(TokenType::With)
36191                    && self.current + 1 < self.tokens.len()
36192                    && self.tokens[self.current + 1]
36193                        .text
36194                        .eq_ignore_ascii_case("FILL")
36195                {
36196                    self.skip(); // consume WITH
36197                    self.skip(); // consume FILL
36198                    let from_ = if self.match_token(TokenType::From) {
36199                        Some(Box::new(self.parse_or()?))
36200                    } else {
36201                        None
36202                    };
36203                    let to = if self.match_text_seq(&["TO"]) {
36204                        Some(Box::new(self.parse_or()?))
36205                    } else {
36206                        None
36207                    };
36208                    let step = if self.match_text_seq(&["STEP"]) {
36209                        Some(Box::new(self.parse_or()?))
36210                    } else {
36211                        None
36212                    };
36213                    let staleness = if self.match_text_seq(&["STALENESS"]) {
36214                        Some(Box::new(self.parse_or()?))
36215                    } else {
36216                        None
36217                    };
36218                    let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
36219                        if self.match_token(TokenType::LParen) {
36220                            let items = self.parse_expression_list()?;
36221                            self.expect(TokenType::RParen)?;
36222                            if items.len() == 1 {
36223                                Some(Box::new(items.into_iter().next().unwrap()))
36224                            } else {
36225                                Some(Box::new(Expression::Tuple(Box::new(
36226                                    crate::expressions::Tuple { expressions: items },
36227                                ))))
36228                            }
36229                        } else {
36230                            None
36231                        }
36232                    } else {
36233                        None
36234                    };
36235                    Some(Box::new(WithFill {
36236                        from_,
36237                        to,
36238                        step,
36239                        staleness,
36240                        interpolate,
36241                    }))
36242                } else {
36243                    None
36244                };
36245                exprs.push(Ordered {
36246                    this: expr,
36247                    desc,
36248                    nulls_first,
36249                    explicit_asc,
36250                    with_fill,
36251                });
36252                if !self.match_token(TokenType::Comma) {
36253                    break;
36254                }
36255            }
36256            exprs
36257        } else {
36258            Vec::new()
36259        };
36260
36261        // Parse window frame
36262        let frame = self.parse_window_frame()?;
36263
36264        self.expect(TokenType::RParen)?;
36265
36266        Ok(Over {
36267            window_name,
36268            partition_by,
36269            order_by,
36270            frame,
36271            alias: None,
36272        })
36273    }
36274
36275    /// Parse window frame specification (ROWS/RANGE/GROUPS BETWEEN ...)
36276    fn parse_window_frame(&mut self) -> Result<Option<WindowFrame>> {
36277        let (kind, kind_text) = if self.match_token(TokenType::Rows) {
36278            (
36279                WindowFrameKind::Rows,
36280                self.tokens[self.current - 1].text.clone(),
36281            )
36282        } else if self.match_token(TokenType::Range) {
36283            (
36284                WindowFrameKind::Range,
36285                self.tokens[self.current - 1].text.clone(),
36286            )
36287        } else if self.match_token(TokenType::Groups) {
36288            (
36289                WindowFrameKind::Groups,
36290                self.tokens[self.current - 1].text.clone(),
36291            )
36292        } else {
36293            return Ok(None);
36294        };
36295
36296        // Parse BETWEEN or single bound
36297        let (start, start_side_text, end, end_side_text) = if self.match_token(TokenType::Between) {
36298            let (start, st) = self.parse_window_frame_bound()?;
36299            self.expect(TokenType::And)?;
36300            let (end, et) = self.parse_window_frame_bound()?;
36301            (start, st, Some(end), et)
36302        } else {
36303            let (start, st) = self.parse_window_frame_bound()?;
36304            (start, st, None, None)
36305        };
36306
36307        // Parse optional EXCLUDE clause
36308        let exclude = if self.match_token(TokenType::Exclude) {
36309            if self.match_token(TokenType::Current) {
36310                self.expect(TokenType::Row)?;
36311                Some(WindowFrameExclude::CurrentRow)
36312            } else if self.match_token(TokenType::Group) {
36313                Some(WindowFrameExclude::Group)
36314            } else if self.match_token(TokenType::Ties) {
36315                Some(WindowFrameExclude::Ties)
36316            } else if self.match_token(TokenType::No) {
36317                self.expect(TokenType::Others)?;
36318                Some(WindowFrameExclude::NoOthers)
36319            } else {
36320                return Err(self
36321                    .parse_error("Expected CURRENT ROW, GROUP, TIES, or NO OTHERS after EXCLUDE"));
36322            }
36323        } else {
36324            None
36325        };
36326
36327        Ok(Some(WindowFrame {
36328            kind,
36329            start,
36330            end,
36331            exclude,
36332            kind_text: Some(kind_text),
36333            start_side_text,
36334            end_side_text,
36335        }))
36336    }
36337
36338    /// Parse a window frame bound, returning the bound and the original text of the side keyword
36339    fn parse_window_frame_bound(&mut self) -> Result<(WindowFrameBound, Option<String>)> {
36340        if self.match_token(TokenType::Current) {
36341            self.expect(TokenType::Row)?;
36342            Ok((WindowFrameBound::CurrentRow, None))
36343        } else if self.match_token(TokenType::Unbounded) {
36344            if self.match_token(TokenType::Preceding) {
36345                let text = self.tokens[self.current - 1].text.clone();
36346                Ok((WindowFrameBound::UnboundedPreceding, Some(text)))
36347            } else if self.match_token(TokenType::Following) {
36348                let text = self.tokens[self.current - 1].text.clone();
36349                Ok((WindowFrameBound::UnboundedFollowing, Some(text)))
36350            } else {
36351                Err(self.parse_error("Expected PRECEDING or FOLLOWING after UNBOUNDED"))
36352            }
36353        } else if self.match_token(TokenType::Preceding) {
36354            let text = self.tokens[self.current - 1].text.clone();
36355            // PRECEDING [value] (inverted syntax for some dialects)
36356            // If no value follows (e.g., just "PRECEDING" or "PRECEDING)"), use BarePreceding
36357            if self.check(TokenType::RParen) || self.check(TokenType::Comma) {
36358                Ok((WindowFrameBound::BarePreceding, Some(text)))
36359            } else {
36360                let expr = self.parse_primary()?;
36361                Ok((WindowFrameBound::Preceding(Box::new(expr)), Some(text)))
36362            }
36363        } else if self.match_token(TokenType::Following) {
36364            let text = self.tokens[self.current - 1].text.clone();
36365            // FOLLOWING [value] (inverted syntax for some dialects)
36366            // If no value follows (e.g., just "FOLLOWING" or "FOLLOWING)"), use BareFollowing
36367            if self.check(TokenType::RParen) || self.check(TokenType::Comma) {
36368                Ok((WindowFrameBound::BareFollowing, Some(text)))
36369            } else {
36370                let expr = self.parse_primary()?;
36371                Ok((WindowFrameBound::Following(Box::new(expr)), Some(text)))
36372            }
36373        } else {
36374            // <expr> PRECEDING | FOLLOWING (standard syntax)
36375            // Use parse_addition to handle expressions like 1 + 1 PRECEDING
36376            let expr = self.parse_addition()?;
36377            if self.match_token(TokenType::Preceding) {
36378                let text = self.tokens[self.current - 1].text.clone();
36379                Ok((WindowFrameBound::Preceding(Box::new(expr)), Some(text)))
36380            } else if self.match_token(TokenType::Following) {
36381                let text = self.tokens[self.current - 1].text.clone();
36382                Ok((WindowFrameBound::Following(Box::new(expr)), Some(text)))
36383            } else {
36384                // Bare numeric bounds without PRECEDING/FOLLOWING
36385                // (e.g., RANGE BETWEEN 1 AND 3)
36386                Ok((WindowFrameBound::Value(Box::new(expr)), None))
36387            }
36388        }
36389    }
36390
36391    /// Try to parse INTERVAL expression. Returns None if INTERVAL should be treated as identifier.
36392    fn try_parse_interval(&mut self) -> Result<Option<Expression>> {
36393        self.try_parse_interval_internal(true)
36394    }
36395
36396    /// Internal interval parsing that optionally matches the INTERVAL keyword.
36397    /// When match_interval is false, it parses a chained interval value-unit pair
36398    /// without requiring the INTERVAL keyword.
36399    fn try_parse_interval_internal(&mut self, match_interval: bool) -> Result<Option<Expression>> {
36400        let start_pos = self.current;
36401
36402        // Consume the INTERVAL keyword if required
36403        if match_interval {
36404            if !self.check(TokenType::Interval) {
36405                return Ok(None);
36406            }
36407            self.expect(TokenType::Interval)?;
36408
36409            // Check if next token is an operator - if so, INTERVAL is used as identifier
36410            if self.check(TokenType::Eq)
36411                || self.check(TokenType::Neq)
36412                || self.check(TokenType::Lt)
36413                || self.check(TokenType::Gt)
36414                || self.check(TokenType::Lte)
36415                || self.check(TokenType::Gte)
36416                || self.check(TokenType::And)
36417                || self.check(TokenType::Or)
36418                || self.check(TokenType::Is)
36419                || self.check(TokenType::In)
36420                || self.check(TokenType::Like)
36421                || self.check(TokenType::ILike)
36422                || self.check(TokenType::Between)
36423                || self.check(TokenType::Then)
36424                || self.check(TokenType::Else)
36425                || self.check(TokenType::When)
36426                || self.check(TokenType::End)
36427                || self.check(TokenType::Comma)
36428                || self.check(TokenType::RParen)
36429                || self.check(TokenType::DColon)
36430            {
36431                // INTERVAL is used as identifier
36432                self.current = start_pos;
36433                return Ok(None);
36434            }
36435        }
36436
36437        // Parse the value after INTERVAL
36438        // IMPORTANT: For string literals, don't use parse_primary() because it calls
36439        // maybe_parse_subscript() which would consume postfix operators like ::TYPE.
36440        // Those should be applied to the full INTERVAL expression, not just the value inside.
36441        // e.g., INTERVAL '1 hour'::VARCHAR should be CAST(INTERVAL '1 hour' AS VARCHAR)
36442        //       not INTERVAL CAST('1 hour' AS VARCHAR)
36443        // For non-string values, use parse_addition() to handle expressions like
36444        // INTERVAL 2 * 2 MONTH or INTERVAL DAYOFMONTH(dt) - 1 DAY (MySQL syntax)
36445        // This matches Python sqlglot's _parse_term() behavior which handles +, -, *, /, %
36446        let value = if self.check(TokenType::String) {
36447            let token = self.advance();
36448            Some(Expression::Literal(Box::new(Literal::String(token.text))))
36449        } else if !self.is_at_end() && !self.is_statement_terminator() {
36450            Some(self.parse_addition()?)
36451        } else {
36452            None
36453        };
36454
36455        // Check if we should treat INTERVAL as an identifier instead
36456        // This happens when:
36457        // - No value was parsed, OR
36458        // - Value is an unqualified, unquoted column reference AND
36459        //   what follows is NOT a valid interval unit
36460        if let Some(ref val) = value {
36461            if let Expression::Column(col) = val {
36462                // Column without table qualifier
36463                if col.table.is_none() {
36464                    // Check if identifier is quoted
36465                    let is_quoted = col.name.quoted;
36466                    if !is_quoted {
36467                        // Check if next token is a valid interval unit
36468                        if !self.is_valid_interval_unit() && !self.check(TokenType::As) {
36469                            // Backtrack - INTERVAL is used as identifier
36470                            self.current = start_pos;
36471                            return Ok(None);
36472                        }
36473                    }
36474                }
36475            } else if let Expression::Identifier(id) = val {
36476                // Bare identifier without table qualifier
36477                let is_quoted = id.quoted;
36478                if !is_quoted {
36479                    // Check if next token is a valid interval unit
36480                    if !self.is_valid_interval_unit() && !self.check(TokenType::As) {
36481                        // Backtrack - INTERVAL is used as identifier
36482                        self.current = start_pos;
36483                        return Ok(None);
36484                    }
36485                }
36486            }
36487        } else if self.is_at_end() || self.is_statement_terminator() {
36488            // No value, and at end/terminator - INTERVAL is an identifier
36489            self.current = start_pos;
36490            return Ok(None);
36491        }
36492
36493        // Now parse the optional unit
36494        let mut unit = self.try_parse_interval_unit()?;
36495
36496        // Split compound interval strings like '1 day' into value '1' and unit DAY
36497        // This matches Python sqlglot's INTERVAL_STRING_RE behavior
36498        // Only apply in generic mode -- dialects like PostgreSQL preserve compound strings
36499        let is_generic = self.config.dialect.is_none()
36500            || matches!(
36501                self.config.dialect,
36502                Some(crate::dialects::DialectType::Generic)
36503            );
36504        let value = if unit.is_none() && is_generic {
36505            if let Some(Expression::Literal(ref lit)) = value {
36506                if let Literal::String(ref s) = lit.as_ref() {
36507                    let trimmed = s.trim();
36508                    // Match pattern: optional negative sign, digits (optional decimal), space(s), alpha unit
36509                    let mut split_pos = None;
36510                    let mut found_space = false;
36511                    let bytes = trimmed.as_bytes();
36512                    let mut i = 0;
36513                    // Skip optional negative sign
36514                    if i < bytes.len() && bytes[i] == b'-' {
36515                        i += 1;
36516                    }
36517                    // Expect digits
36518                    let digit_start = i;
36519                    while i < bytes.len() && bytes[i].is_ascii_digit() {
36520                        i += 1;
36521                    }
36522                    if i > digit_start {
36523                        // Optional decimal part
36524                        if i < bytes.len() && bytes[i] == b'.' {
36525                            i += 1;
36526                            while i < bytes.len() && bytes[i].is_ascii_digit() {
36527                                i += 1;
36528                            }
36529                        }
36530                        // Expect whitespace
36531                        let space_start = i;
36532                        while i < bytes.len() && bytes[i].is_ascii_whitespace() {
36533                            i += 1;
36534                        }
36535                        if i > space_start {
36536                            found_space = true;
36537                            split_pos = Some(i);
36538                        }
36539                    }
36540                    if found_space {
36541                        if let Some(pos) = split_pos {
36542                            let unit_text = &trimmed[pos..];
36543                            // Verify it's all alpha
36544                            if !unit_text.is_empty()
36545                                && unit_text.chars().all(|c| c.is_ascii_alphabetic())
36546                            {
36547                                let num_part = trimmed[..pos].trim_end().to_string();
36548                                let unit_upper = unit_text.to_ascii_uppercase();
36549                                // Try to parse as interval unit
36550                                if let Some(parsed_unit) =
36551                                    Self::parse_interval_unit_from_string(&unit_upper)
36552                                {
36553                                    // Check if the original text had an 'S' suffix (plural)
36554                                    let is_plural = unit_upper.ends_with('S');
36555                                    unit = Some(IntervalUnitSpec::Simple {
36556                                        unit: parsed_unit,
36557                                        use_plural: is_plural,
36558                                    });
36559                                    Some(Expression::Literal(Box::new(Literal::String(num_part))))
36560                                } else {
36561                                    value
36562                                }
36563                            } else {
36564                                value
36565                            }
36566                        } else {
36567                            value
36568                        }
36569                    } else {
36570                        value
36571                    }
36572                } else {
36573                    None
36574                }
36575            } else {
36576                value
36577            }
36578        } else {
36579            value
36580        };
36581
36582        // Convert number literals to string literals in intervals (canonical form).
36583        // Most dialects support INTERVAL '5' DAY, so we normalize to this form
36584        // for easier transpilation. This matches Python sqlglot's behavior in
36585        // _parse_interval_span: "if this and this.is_number: this = exp.Literal.string(this.to_py())"
36586        let value = match value {
36587            Some(Expression::Literal(lit))
36588                if unit.is_some() && matches!(lit.as_ref(), Literal::Number(_)) =>
36589            {
36590                let Literal::Number(n) = lit.as_ref() else {
36591                    unreachable!()
36592                };
36593                Some(Expression::Literal(Box::new(Literal::String(n.clone()))))
36594            }
36595            other => other,
36596        };
36597
36598        let interval = Expression::Interval(Box::new(Interval { this: value, unit }));
36599
36600        // Support for chained multi-unit interval syntax (Spark/Hive):
36601        // INTERVAL '5' HOURS '30' MINUTES -> INTERVAL '5' HOURS + INTERVAL '30' MINUTES
36602        // This is done by optionally matching a PLUS sign, and if followed by
36603        // another string or number (without INTERVAL keyword), recursively parsing
36604        // and creating an Add expression.
36605        let before_plus = self.current;
36606        let has_plus = self.match_token(TokenType::Plus);
36607
36608        // Check if followed by a STRING or NUMBER (potential chained interval)
36609        if self.check(TokenType::String) || self.check(TokenType::Number) {
36610            // Recursively parse the chained interval without the INTERVAL keyword
36611            if let Some(next_interval) = self.try_parse_interval_internal(false)? {
36612                return Ok(Some(Expression::Add(Box::new(BinaryOp::new(
36613                    interval,
36614                    next_interval,
36615                )))));
36616            }
36617        }
36618
36619        // If we consumed a PLUS but didn't find a chained interval, backtrack
36620        if has_plus {
36621            self.current = before_plus;
36622        }
36623
36624        Ok(Some(interval))
36625    }
36626
36627    /// Check if current token is a valid interval unit
36628    fn is_valid_interval_unit(&self) -> bool {
36629        if self.is_at_end() {
36630            return false;
36631        }
36632        let text = self.peek().text.to_ascii_uppercase();
36633        matches!(
36634            text.as_str(),
36635            "YEAR"
36636                | "YEARS"
36637                | "MONTH"
36638                | "MONTHS"
36639                | "DAY"
36640                | "DAYS"
36641                | "HOUR"
36642                | "HOURS"
36643                | "MINUTE"
36644                | "MINUTES"
36645                | "SECOND"
36646                | "SECONDS"
36647                | "MILLISECOND"
36648                | "MILLISECONDS"
36649                | "MICROSECOND"
36650                | "MICROSECONDS"
36651                | "NANOSECOND"
36652                | "NANOSECONDS"
36653                | "WEEK"
36654                | "WEEKS"
36655                | "QUARTER"
36656                | "QUARTERS"
36657        )
36658    }
36659
36660    /// Check if current token terminates a statement/expression context
36661    fn is_statement_terminator(&self) -> bool {
36662        if self.is_at_end() {
36663            return true;
36664        }
36665        matches!(
36666            self.peek().token_type,
36667            TokenType::Semicolon
36668                | TokenType::RParen
36669                | TokenType::RBracket
36670                | TokenType::Comma
36671                | TokenType::From
36672                | TokenType::Where
36673                | TokenType::GroupBy
36674                | TokenType::Having
36675                | TokenType::OrderBy
36676                | TokenType::Limit
36677                | TokenType::Union
36678                | TokenType::Intersect
36679                | TokenType::Except
36680                | TokenType::End
36681                | TokenType::Then
36682                | TokenType::Else
36683                | TokenType::When
36684        )
36685    }
36686
36687    /// Try to parse interval unit - returns None if no unit present
36688    fn try_parse_interval_unit(&mut self) -> Result<Option<IntervalUnitSpec>> {
36689        // First, check if there's a function (like CURRENT_DATE, CAST(...))
36690        if self.is_function_start() {
36691            let func = self.parse_primary()?;
36692            return Ok(Some(IntervalUnitSpec::Expr(Box::new(func))));
36693        }
36694
36695        // Try to parse a simple unit or span
36696        if let Some((unit, use_plural)) = self.try_parse_simple_interval_unit()? {
36697            // Check for "TO" to make it a span (e.g., YEAR TO MONTH)
36698            // Use lookahead to avoid consuming TO when it's part of WITH FILL
36699            if self.check_keyword_text("TO") {
36700                let saved = self.current;
36701                self.skip(); // consume TO
36702                if let Some((end_unit, _)) = self.try_parse_simple_interval_unit()? {
36703                    return Ok(Some(IntervalUnitSpec::Span(IntervalSpan {
36704                        this: unit,
36705                        expression: end_unit,
36706                    })));
36707                } else {
36708                    // Not followed by a valid interval unit — backtrack
36709                    self.current = saved;
36710                }
36711            }
36712            return Ok(Some(IntervalUnitSpec::Simple { unit, use_plural }));
36713        }
36714
36715        // No unit found
36716        Ok(None)
36717    }
36718
36719    /// Parse an interval unit from a string (used for splitting compound interval strings)
36720    fn parse_interval_unit_from_string(s: &str) -> Option<IntervalUnit> {
36721        // Strip trailing 'S' for plural forms
36722        let base = if s.ends_with('S') && s.len() > 1 {
36723            &s[..s.len() - 1]
36724        } else {
36725            s
36726        };
36727        match base {
36728            "YEAR" => Some(IntervalUnit::Year),
36729            "MONTH" => Some(IntervalUnit::Month),
36730            "DAY" => Some(IntervalUnit::Day),
36731            "HOUR" => Some(IntervalUnit::Hour),
36732            "MINUTE" => Some(IntervalUnit::Minute),
36733            "SECOND" => Some(IntervalUnit::Second),
36734            "MILLISECOND" => Some(IntervalUnit::Millisecond),
36735            "MICROSECOND" => Some(IntervalUnit::Microsecond),
36736            "QUARTER" => Some(IntervalUnit::Quarter),
36737            "WEEK" => Some(IntervalUnit::Week),
36738            _ => None,
36739        }
36740    }
36741
36742    /// Try to parse a simple interval unit (YEAR, MONTH, etc.) - returns (unit, is_plural)
36743    fn try_parse_simple_interval_unit(&mut self) -> Result<Option<(IntervalUnit, bool)>> {
36744        if self.is_at_end() {
36745            return Ok(None);
36746        }
36747
36748        let text_upper = self.peek().text.to_ascii_uppercase();
36749        let result = match text_upper.as_str() {
36750            "YEAR" => Some((IntervalUnit::Year, false)),
36751            "YEARS" => Some((IntervalUnit::Year, true)),
36752            "MONTH" => Some((IntervalUnit::Month, false)),
36753            "MONTHS" => Some((IntervalUnit::Month, true)),
36754            "DAY" => Some((IntervalUnit::Day, false)),
36755            "DAYS" => Some((IntervalUnit::Day, true)),
36756            "HOUR" => Some((IntervalUnit::Hour, false)),
36757            "HOURS" => Some((IntervalUnit::Hour, true)),
36758            "MINUTE" => Some((IntervalUnit::Minute, false)),
36759            "MINUTES" => Some((IntervalUnit::Minute, true)),
36760            "SECOND" => Some((IntervalUnit::Second, false)),
36761            "SECONDS" => Some((IntervalUnit::Second, true)),
36762            "MILLISECOND" => Some((IntervalUnit::Millisecond, false)),
36763            "MILLISECONDS" => Some((IntervalUnit::Millisecond, true)),
36764            "MICROSECOND" => Some((IntervalUnit::Microsecond, false)),
36765            "MICROSECONDS" => Some((IntervalUnit::Microsecond, true)),
36766            "NANOSECOND" => Some((IntervalUnit::Nanosecond, false)),
36767            "NANOSECONDS" => Some((IntervalUnit::Nanosecond, true)),
36768            "QUARTER" => Some((IntervalUnit::Quarter, false)),
36769            "QUARTERS" => Some((IntervalUnit::Quarter, true)),
36770            "WEEK" => Some((IntervalUnit::Week, false)),
36771            "WEEKS" => Some((IntervalUnit::Week, true)),
36772            _ => None,
36773        };
36774
36775        if result.is_some() {
36776            self.skip(); // consume the unit token
36777        }
36778
36779        Ok(result)
36780    }
36781
36782    /// Check if current position starts a function call or no-paren function
36783    fn is_function_start(&self) -> bool {
36784        if self.is_at_end() {
36785            return false;
36786        }
36787        let token_type = self.peek().token_type;
36788
36789        // Check NO_PAREN_FUNCTIONS configuration map
36790        if NO_PAREN_FUNCTIONS.contains(&token_type) {
36791            if !matches!(
36792                self.config.dialect,
36793                Some(crate::dialects::DialectType::ClickHouse)
36794            ) || token_type != TokenType::CurrentTimestamp
36795            {
36796                return true;
36797            }
36798        }
36799
36800        // Cast functions are always functions
36801        if matches!(
36802            token_type,
36803            TokenType::Cast | TokenType::TryCast | TokenType::SafeCast
36804        ) {
36805            return true;
36806        }
36807
36808        // Check NO_PAREN_FUNCTION_NAMES for string-based lookup
36809        // (handles cases where functions are tokenized as Var/Identifier)
36810        let text_upper = self.peek().text.to_ascii_uppercase();
36811        if crate::function_registry::is_no_paren_function_name_upper(text_upper.as_str()) {
36812            if !matches!(
36813                self.config.dialect,
36814                Some(crate::dialects::DialectType::ClickHouse)
36815            ) || text_upper.as_str() != "CURRENT_TIMESTAMP"
36816            {
36817                return true;
36818            }
36819        }
36820
36821        // Identifier followed by left paren (function call)
36822        if self.is_identifier_token() && self.check_next(TokenType::LParen) {
36823            return true;
36824        }
36825
36826        false
36827    }
36828
36829    /// Try to parse Oracle interval span after an expression.
36830    /// Syntax: (expr) DAY[(precision)] TO SECOND[(fractional_precision)]
36831    /// This is used in Oracle for interval expressions like:
36832    /// (SYSTIMESTAMP - order_date) DAY(9) TO SECOND(3)
36833    fn try_parse_oracle_interval_span(&mut self, expr: Expression) -> Result<Expression> {
36834        let start_pos = self.current;
36835
36836        // Check if current token is an interval unit keyword (DAY, HOUR, MINUTE, SECOND, YEAR, MONTH)
36837        let start_unit_name = if !self.is_at_end() {
36838            let text = self.peek().text.to_ascii_uppercase();
36839            if matches!(
36840                text.as_str(),
36841                "DAY" | "HOUR" | "MINUTE" | "SECOND" | "YEAR" | "MONTH"
36842            ) {
36843                Some(text)
36844            } else {
36845                None
36846            }
36847        } else {
36848            None
36849        };
36850
36851        if start_unit_name.is_none() {
36852            return Ok(expr);
36853        }
36854
36855        let start_unit_name = start_unit_name.unwrap();
36856        self.skip(); // consume the unit keyword
36857
36858        // Parse optional precision: DAY(9) or just DAY
36859        let start_unit = if self.match_token(TokenType::LParen) {
36860            // Parse precision
36861            let precision = self.parse_expression()?;
36862            self.expect(TokenType::RParen)?;
36863            // Create a function-like expression for the unit with precision
36864            Expression::Anonymous(Box::new(Anonymous {
36865                this: Box::new(Expression::Identifier(Identifier {
36866                    name: start_unit_name.clone(),
36867                    quoted: false,
36868                    trailing_comments: Vec::new(),
36869                    span: None,
36870                })),
36871                expressions: vec![precision],
36872            }))
36873        } else {
36874            // Simple unit without precision
36875            Expression::Var(Box::new(Var {
36876                this: start_unit_name,
36877            }))
36878        };
36879
36880        // Check for TO keyword
36881        if !self.match_keyword("TO") {
36882            // Not an interval span, backtrack
36883            self.current = start_pos;
36884            return Ok(expr);
36885        }
36886
36887        // Parse end unit
36888        let end_unit_name = if !self.is_at_end() {
36889            let text = self.peek().text.to_ascii_uppercase();
36890            if matches!(
36891                text.as_str(),
36892                "DAY" | "HOUR" | "MINUTE" | "SECOND" | "YEAR" | "MONTH"
36893            ) {
36894                Some(text)
36895            } else {
36896                None
36897            }
36898        } else {
36899            None
36900        };
36901
36902        let end_unit_name = match end_unit_name {
36903            Some(name) => name,
36904            None => {
36905                // No valid end unit, backtrack
36906                self.current = start_pos;
36907                return Ok(expr);
36908            }
36909        };
36910
36911        self.skip(); // consume the end unit keyword
36912
36913        // Parse optional precision for end unit: SECOND(3) or just SECOND
36914        let end_unit = if self.match_token(TokenType::LParen) {
36915            // Parse fractional precision
36916            let precision = self.parse_expression()?;
36917            self.expect(TokenType::RParen)?;
36918            // Create a function-like expression for the unit with precision
36919            Expression::Anonymous(Box::new(Anonymous {
36920                this: Box::new(Expression::Identifier(Identifier {
36921                    name: end_unit_name.clone(),
36922                    quoted: false,
36923                    trailing_comments: Vec::new(),
36924                    span: None,
36925                })),
36926                expressions: vec![precision],
36927            }))
36928        } else {
36929            // Simple unit without precision
36930            Expression::Var(Box::new(Var {
36931                this: end_unit_name,
36932            }))
36933        };
36934
36935        // Create an Interval expression with ExprSpan unit
36936        Ok(Expression::Interval(Box::new(Interval {
36937            this: Some(expr),
36938            unit: Some(IntervalUnitSpec::ExprSpan(IntervalSpanExpr {
36939                this: Box::new(start_unit),
36940                expression: Box::new(end_unit),
36941            })),
36942        })))
36943    }
36944
36945    /// Check if the current position starts a typed column list (for table function aliases)
36946    /// like: (col1 type1, col2 type2)
36947    /// This peeks ahead to see if the first column name is followed by a type token,
36948    /// rather than a comma or closing paren (which would indicate simple column aliases).
36949    /// Used for PostgreSQL functions like JSON_TO_RECORDSET that have typed column definitions.
36950    fn check_typed_column_list(&self) -> bool {
36951        // We're positioned after '(' - check pattern: identifier type
36952        // If we see identifier followed by something that's not ',' or ')', it's typed
36953        if self.is_at_end() {
36954            return false;
36955        }
36956
36957        // Check if current is an identifier (column name)
36958        let has_identifier = self.check(TokenType::Identifier)
36959            || self.check(TokenType::QuotedIdentifier)
36960            || self.check(TokenType::Var);
36961
36962        if !has_identifier {
36963            return false;
36964        }
36965
36966        // Look at next token (after the identifier)
36967        let next_pos = self.current + 1;
36968        if next_pos >= self.tokens.len() {
36969            return false;
36970        }
36971
36972        let next_token = &self.tokens[next_pos];
36973
36974        // If next token is comma or rparen, it's simple column aliases
36975        if next_token.token_type == TokenType::Comma || next_token.token_type == TokenType::RParen {
36976            return false;
36977        }
36978
36979        // If next token could be a type name (identifier, var, or type keyword), it's typed columns
36980        // Check for type tokens or identifiers that could be type names
36981        TYPE_TOKENS.contains(&next_token.token_type)
36982            || next_token.token_type == TokenType::Identifier
36983            || next_token.token_type == TokenType::Var
36984    }
36985
36986    /// Check if current token is a no-paren function
36987    fn is_no_paren_function(&self) -> bool {
36988        if self.is_at_end() {
36989            return false;
36990        }
36991        let token_type = self.peek().token_type;
36992        if NO_PAREN_FUNCTIONS.contains(&token_type) {
36993            if !matches!(
36994                self.config.dialect,
36995                Some(crate::dialects::DialectType::ClickHouse)
36996            ) || token_type != TokenType::CurrentTimestamp
36997            {
36998                return true;
36999            }
37000        }
37001        let text_upper = self.peek().text.to_ascii_uppercase();
37002        if crate::function_registry::is_no_paren_function_name_upper(text_upper.as_str()) {
37003            if !matches!(
37004                self.config.dialect,
37005                Some(crate::dialects::DialectType::ClickHouse)
37006            ) || text_upper.as_str() != "CURRENT_TIMESTAMP"
37007            {
37008                return true;
37009            }
37010        }
37011        false
37012    }
37013
37014    /// Match a keyword by text (case-insensitive)
37015    fn match_keyword(&mut self, keyword: &str) -> bool {
37016        if self.is_at_end() {
37017            return false;
37018        }
37019        if self.peek().text.eq_ignore_ascii_case(keyword) {
37020            self.skip();
37021            true
37022        } else {
37023            false
37024        }
37025    }
37026
37027    /// Match a sequence of keywords by text (case-insensitive)
37028    fn match_text_seq(&mut self, keywords: &[&str]) -> bool {
37029        for (i, &kw) in keywords.iter().enumerate() {
37030            if self.current + i >= self.tokens.len() {
37031                return false;
37032            }
37033            if !self.tokens[self.current + i].text.eq_ignore_ascii_case(kw) {
37034                return false;
37035            }
37036        }
37037        self.current += keywords.len();
37038        true
37039    }
37040
37041    /// Check (without consuming) if the next tokens match a sequence of keywords by text (case-insensitive)
37042    fn check_text_seq(&self, keywords: &[&str]) -> bool {
37043        for (i, &kw) in keywords.iter().enumerate() {
37044            if self.current + i >= self.tokens.len() {
37045                return false;
37046            }
37047            if !self.tokens[self.current + i].text.eq_ignore_ascii_case(kw) {
37048                return false;
37049            }
37050        }
37051        true
37052    }
37053
37054    /// Match any of the given texts (case-insensitive)
37055    fn match_texts(&mut self, texts: &[&str]) -> bool {
37056        if self.is_at_end() {
37057            return false;
37058        }
37059        for text in texts {
37060            if self.peek().text.eq_ignore_ascii_case(text) {
37061                self.skip();
37062                return true;
37063            }
37064        }
37065        false
37066    }
37067
37068    /// Parse CASE expression
37069    fn parse_case(&mut self) -> Result<Expression> {
37070        self.expect(TokenType::Case)?;
37071        // Capture trailing comments from the CASE keyword (e.g., CASE /* test */ WHEN ...)
37072        let case_comments = self.previous_trailing_comments().to_vec();
37073
37074        // Check for simple CASE (CASE expr WHEN ...)
37075        let operand = if !self.check(TokenType::When) {
37076            Some(self.parse_expression()?)
37077        } else {
37078            None
37079        };
37080
37081        let mut whens = Vec::new();
37082        while self.match_token(TokenType::When) {
37083            let condition = self.parse_expression()?;
37084            self.expect(TokenType::Then)?;
37085            let mut result = self.parse_expression()?;
37086            // ClickHouse: CASE WHEN x THEN 1 as alias WHEN y THEN alias / 2 END
37087            // Aliases can appear in CASE THEN expressions
37088            if matches!(
37089                self.config.dialect,
37090                Some(crate::dialects::DialectType::ClickHouse)
37091            ) && self.match_token(TokenType::As)
37092            {
37093                let alias = self.expect_identifier_or_keyword()?;
37094                result = Expression::Alias(Box::new(Alias {
37095                    this: result,
37096                    alias: Identifier::new(alias),
37097                    column_aliases: Vec::new(),
37098                    pre_alias_comments: Vec::new(),
37099                    trailing_comments: Vec::new(),
37100                    inferred_type: None,
37101                }));
37102            }
37103            whens.push((condition, result));
37104        }
37105
37106        let else_ = if self.match_token(TokenType::Else) {
37107            Some(self.parse_expression()?)
37108        } else {
37109            None
37110        };
37111
37112        self.expect(TokenType::End)?;
37113
37114        Ok(Expression::Case(Box::new(Case {
37115            operand,
37116            whens,
37117            else_,
37118            comments: case_comments,
37119            inferred_type: None,
37120        })))
37121    }
37122
37123    /// Parse CAST expression
37124    fn parse_cast(&mut self) -> Result<Expression> {
37125        self.expect(TokenType::Cast)?;
37126        self.expect(TokenType::LParen)?;
37127        // Use parse_or() instead of parse_expression() to avoid consuming AS
37128        // as an alias (e.g. CAST((1, 2) AS Tuple(a Int8, b Int16)))
37129        // Python sqlglot uses _parse_disjunction() here, which is equivalent.
37130        let expr = self.parse_or()?;
37131
37132        // ClickHouse: ternary operator inside CAST: CAST(cond ? true_val : false_val AS Type)
37133        let expr = if matches!(
37134            self.config.dialect,
37135            Some(crate::dialects::DialectType::ClickHouse)
37136        ) && self.match_token(TokenType::Parameter)
37137        {
37138            if self.check(TokenType::Colon) {
37139                return Err(
37140                    self.parse_error("Expected true expression after ? in ClickHouse ternary")
37141                );
37142            }
37143            let true_value = self.parse_or()?;
37144            let false_value = if self.match_token(TokenType::Colon) {
37145                self.parse_or()?
37146            } else {
37147                Expression::Null(Null)
37148            };
37149            Expression::IfFunc(Box::new(IfFunc {
37150                original_name: None,
37151                condition: expr,
37152                true_value,
37153                false_value: Some(false_value),
37154                inferred_type: None,
37155            }))
37156        } else {
37157            expr
37158        };
37159
37160        // ClickHouse: implicit alias in CAST: cast('1234' lhs AS UInt32) or cast('1234' lhs, 'UInt32')
37161        let expr = self.try_clickhouse_implicit_alias(expr);
37162
37163        // ClickHouse: CAST(expr, 'type_string') or CAST(expr, expression) syntax with comma instead of AS
37164        if matches!(
37165            self.config.dialect,
37166            Some(crate::dialects::DialectType::ClickHouse)
37167        ) && self.match_token(TokenType::Comma)
37168        {
37169            // Parse as expression to handle concat and other operations: CAST(x, 'Str' || 'ing')
37170            let type_expr = self.parse_expression()?;
37171            // ClickHouse: alias on type expr: cast('1234' lhs, 'UInt32' rhs) or cast('1234', 'UInt32' AS rhs)
37172            let type_expr = self.try_clickhouse_func_arg_alias(type_expr);
37173            self.expect(TokenType::RParen)?;
37174            let _trailing_comments = self.previous_trailing_comments().to_vec();
37175            return Ok(Expression::CastToStrType(Box::new(CastToStrType {
37176                this: Box::new(expr),
37177                to: Some(Box::new(type_expr)),
37178            })));
37179        }
37180
37181        self.expect(TokenType::As)?;
37182
37183        // ClickHouse: CAST(expr AS alias AS Type) — inner alias before type
37184        // If the next token is an identifier followed by AS, treat it as an alias
37185        let expr = if matches!(
37186            self.config.dialect,
37187            Some(crate::dialects::DialectType::ClickHouse)
37188        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
37189            && self
37190                .peek_nth(1)
37191                .map_or(false, |t| t.token_type == TokenType::As)
37192        {
37193            let alias = self.expect_identifier_or_keyword_with_quoted()?;
37194            self.expect(TokenType::As)?;
37195            Expression::Alias(Box::new(Alias::new(expr, alias)))
37196        } else if matches!(
37197            self.config.dialect,
37198            Some(crate::dialects::DialectType::ClickHouse)
37199        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
37200            && self
37201                .peek_nth(1)
37202                .map_or(false, |t| t.token_type == TokenType::Comma)
37203        {
37204            // ClickHouse: CAST(expr AS alias, type_string) — alias before comma syntax
37205            let alias = self.expect_identifier_or_keyword_with_quoted()?;
37206            let expr = Expression::Alias(Box::new(Alias::new(expr, alias)));
37207            self.expect(TokenType::Comma)?;
37208            let type_expr = self.parse_expression()?;
37209            let type_expr = self.try_clickhouse_func_arg_alias(type_expr);
37210            self.expect(TokenType::RParen)?;
37211            let _trailing_comments = self.previous_trailing_comments().to_vec();
37212            return Ok(Expression::CastToStrType(Box::new(CastToStrType {
37213                this: Box::new(expr),
37214                to: Some(Box::new(type_expr)),
37215            })));
37216        } else {
37217            expr
37218        };
37219
37220        // Teradata: CAST(x AS FORMAT 'fmt') (no explicit type)
37221        if matches!(
37222            self.config.dialect,
37223            Some(crate::dialects::DialectType::Teradata)
37224        ) && self.match_token(TokenType::Format)
37225        {
37226            let format = Some(Box::new(self.parse_expression()?));
37227            self.expect(TokenType::RParen)?;
37228            let trailing_comments = self.previous_trailing_comments().to_vec();
37229            return Ok(Expression::Cast(Box::new(Cast {
37230                this: expr,
37231                to: DataType::Unknown,
37232                trailing_comments,
37233                double_colon_syntax: false,
37234                format,
37235                default: None,
37236                inferred_type: None,
37237            })));
37238        }
37239
37240        let data_type = self.parse_data_type()?;
37241
37242        // Parse optional DEFAULT ... ON CONVERSION ERROR (Oracle)
37243        // CAST(x AS type DEFAULT val ON CONVERSION ERROR)
37244        let default = if self.match_token(TokenType::Default) {
37245            let default_val = self.parse_primary()?;
37246            // Expect "ON CONVERSION ERROR"
37247            if !self.match_text_seq(&["ON", "CONVERSION", "ERROR"]) {
37248                return Err(self.parse_error("Expected ON CONVERSION ERROR"));
37249            }
37250            Some(Box::new(default_val))
37251        } else {
37252            None
37253        };
37254
37255        // Parse optional FORMAT clause for BigQuery: CAST(x AS STRING FORMAT 'format_string')
37256        // Or for Oracle with comma: CAST(x AS DATE DEFAULT NULL ON CONVERSION ERROR, 'format')
37257        // FORMAT string may be optionally wrapped in parentheses: FORMAT ('YYYY') -> FORMAT 'YYYY'
37258        let format = if self.match_token(TokenType::Format) {
37259            let wrapped = self.match_token(TokenType::LParen);
37260            let fmt_expr = self.parse_primary()?;
37261            if wrapped {
37262                self.expect(TokenType::RParen)?;
37263            }
37264            // Check for AT TIME ZONE after format string
37265            let fmt_with_tz = if self.match_text_seq(&["AT", "TIME", "ZONE"]) {
37266                let zone = self.parse_primary()?;
37267                Expression::AtTimeZone(Box::new(crate::expressions::AtTimeZone {
37268                    this: fmt_expr,
37269                    zone,
37270                }))
37271            } else {
37272                fmt_expr
37273            };
37274            Some(Box::new(fmt_with_tz))
37275        } else if self.match_token(TokenType::Comma) {
37276            // Oracle date format: CAST(x AS DATE, 'format')
37277            Some(Box::new(self.parse_expression()?))
37278        } else {
37279            None
37280        };
37281
37282        self.expect(TokenType::RParen)?;
37283        let trailing_comments = self.previous_trailing_comments().to_vec();
37284
37285        Ok(Expression::Cast(Box::new(Cast {
37286            this: expr,
37287            to: data_type,
37288            trailing_comments,
37289            double_colon_syntax: false,
37290            format,
37291            default,
37292            inferred_type: None,
37293        })))
37294    }
37295
37296    /// Parse TRY_CAST expression
37297    fn parse_try_cast(&mut self) -> Result<Expression> {
37298        self.expect(TokenType::TryCast)?;
37299        self.expect(TokenType::LParen)?;
37300        let expr = self.parse_or()?;
37301        self.expect(TokenType::As)?;
37302        let data_type = self.parse_data_type()?;
37303
37304        // Parse optional FORMAT clause
37305        let format = if self.match_token(TokenType::Format) {
37306            Some(Box::new(self.parse_expression()?))
37307        } else {
37308            None
37309        };
37310
37311        self.expect(TokenType::RParen)?;
37312        let trailing_comments = self.previous_trailing_comments().to_vec();
37313
37314        Ok(Expression::TryCast(Box::new(Cast {
37315            this: expr,
37316            to: data_type,
37317            trailing_comments,
37318            double_colon_syntax: false,
37319            format,
37320            default: None,
37321            inferred_type: None,
37322        })))
37323    }
37324
37325    /// Parse SAFE_CAST expression (BigQuery)
37326    fn parse_safe_cast(&mut self) -> Result<Expression> {
37327        self.expect(TokenType::SafeCast)?;
37328        self.expect(TokenType::LParen)?;
37329        let expr = self.parse_or()?;
37330        self.expect(TokenType::As)?;
37331        let data_type = self.parse_data_type()?;
37332
37333        // Parse optional FORMAT clause
37334        let format = if self.match_token(TokenType::Format) {
37335            Some(Box::new(self.parse_expression()?))
37336        } else {
37337            None
37338        };
37339
37340        self.expect(TokenType::RParen)?;
37341        let trailing_comments = self.previous_trailing_comments().to_vec();
37342
37343        Ok(Expression::SafeCast(Box::new(Cast {
37344            this: expr,
37345            to: data_type,
37346            trailing_comments,
37347            double_colon_syntax: false,
37348            format,
37349            default: None,
37350            inferred_type: None,
37351        })))
37352    }
37353
37354    /// Parse a data type
37355    fn parse_data_type(&mut self) -> Result<DataType> {
37356        // Handle special token types that represent data type keywords
37357        // Teradata tokenizes ST_GEOMETRY as TokenType::Geometry
37358        if self.check(TokenType::Geometry) {
37359            let _token = self.advance();
37360            let (subtype, srid) = self.parse_spatial_type_args()?;
37361            return Ok(DataType::Geometry { subtype, srid });
37362        }
37363        // Data types can be keywords (DATE, TIMESTAMP, etc.) or identifiers
37364        let mut raw_name = self.expect_identifier_or_keyword()?;
37365        // Allow dotted custom types like SYSUDTLIB.INT
37366        while self.match_token(TokenType::Dot) {
37367            let part = self.expect_identifier_or_keyword()?;
37368            raw_name.push('.');
37369            raw_name.push_str(&part);
37370        }
37371        let mut name = raw_name.to_ascii_uppercase();
37372
37373        // SQL standard: NATIONAL CHAR/CHARACTER → NCHAR
37374        if name == "NATIONAL" {
37375            let next_upper = if !self.is_at_end() {
37376                self.peek().text.to_ascii_uppercase()
37377            } else {
37378                String::new()
37379            };
37380            if next_upper == "CHAR" || next_upper == "CHARACTER" {
37381                self.skip(); // consume CHAR/CHARACTER
37382                name = "NCHAR".to_string();
37383                // NATIONAL CHARACTER VARYING → NVARCHAR equivalent
37384                if next_upper == "CHARACTER" && self.check_identifier("VARYING") {
37385                    self.skip(); // consume VARYING
37386                    let length = if self.match_token(TokenType::LParen) {
37387                        if self.check(TokenType::RParen) {
37388                            self.skip();
37389                            None
37390                        } else {
37391                            let n = self.expect_number()? as u32;
37392                            self.expect(TokenType::RParen)?;
37393                            Some(n)
37394                        }
37395                    } else {
37396                        None
37397                    };
37398                    return Ok(DataType::VarChar {
37399                        length,
37400                        parenthesized_length: false,
37401                    });
37402                }
37403            }
37404        }
37405
37406        let base_type = match name.as_str() {
37407            "INT" | "INTEGER" => {
37408                // MySQL allows INT(N) for display width; ClickHouse allows INT()
37409                let length = if self.match_token(TokenType::LParen) {
37410                    if self.check(TokenType::RParen) {
37411                        self.skip();
37412                        None
37413                    } else {
37414                        let n = self.expect_number()? as u32;
37415                        self.expect(TokenType::RParen)?;
37416                        Some(n)
37417                    }
37418                } else {
37419                    None
37420                };
37421                let integer_spelling = name == "INTEGER";
37422                Ok(DataType::Int {
37423                    length,
37424                    integer_spelling,
37425                })
37426            }
37427            "BIGINT" => {
37428                // MySQL allows BIGINT(N) for display width; ClickHouse allows BIGINT()
37429                let length = if self.match_token(TokenType::LParen) {
37430                    if self.check(TokenType::RParen) {
37431                        self.skip();
37432                        None
37433                    } else {
37434                        let n = self.expect_number()? as u32;
37435                        self.expect(TokenType::RParen)?;
37436                        Some(n)
37437                    }
37438                } else {
37439                    None
37440                };
37441                Ok(DataType::BigInt { length })
37442            }
37443            "SMALLINT" => {
37444                let length = if self.match_token(TokenType::LParen) {
37445                    if self.check(TokenType::RParen) {
37446                        self.skip();
37447                        None
37448                    } else {
37449                        let n = self.expect_number()? as u32;
37450                        self.expect(TokenType::RParen)?;
37451                        Some(n)
37452                    }
37453                } else {
37454                    None
37455                };
37456                Ok(DataType::SmallInt { length })
37457            }
37458            "TINYINT" => {
37459                let length = if self.match_token(TokenType::LParen) {
37460                    if self.check(TokenType::RParen) {
37461                        self.skip();
37462                        None
37463                    } else {
37464                        let n = self.expect_number()? as u32;
37465                        self.expect(TokenType::RParen)?;
37466                        Some(n)
37467                    }
37468                } else {
37469                    None
37470                };
37471                Ok(DataType::TinyInt { length })
37472            }
37473            "FLOAT" | "REAL" => {
37474                let real_spelling = name == "REAL";
37475                // MySQL allows FLOAT(precision) or FLOAT(precision, scale)
37476                let (precision, scale) = if self.match_token(TokenType::LParen) {
37477                    let p = self.expect_number()? as u32;
37478                    let s = if self.match_token(TokenType::Comma) {
37479                        Some(self.expect_number()? as u32)
37480                    } else {
37481                        None
37482                    };
37483                    self.expect(TokenType::RParen)?;
37484                    (Some(p), s)
37485                } else {
37486                    (None, None)
37487                };
37488                Ok(DataType::Float {
37489                    precision,
37490                    scale,
37491                    real_spelling,
37492                })
37493            }
37494            "BINARY_FLOAT" => {
37495                // Oracle's BINARY_FLOAT -> DataType::Float
37496                Ok(DataType::Float {
37497                    precision: None,
37498                    scale: None,
37499                    real_spelling: false,
37500                })
37501            }
37502            "BINARY_DOUBLE" => {
37503                // Oracle's BINARY_DOUBLE -> DataType::Double
37504                Ok(DataType::Double {
37505                    precision: None,
37506                    scale: None,
37507                })
37508            }
37509            "DOUBLE" => {
37510                // Handle DOUBLE PRECISION (PostgreSQL standard SQL)
37511                let _ = self.match_identifier("PRECISION");
37512                // MySQL allows DOUBLE(precision, scale)
37513                let (precision, scale) = if self.match_token(TokenType::LParen) {
37514                    let p = self.expect_number()? as u32;
37515                    let s = if self.match_token(TokenType::Comma) {
37516                        Some(self.expect_number()? as u32)
37517                    } else {
37518                        None
37519                    };
37520                    self.expect(TokenType::RParen)?;
37521                    (Some(p), s)
37522                } else {
37523                    (None, None)
37524                };
37525                Ok(DataType::Double { precision, scale })
37526            }
37527            "DECIMAL" | "NUMERIC" => {
37528                let (precision, scale) = if self.match_token(TokenType::LParen) {
37529                    let p = self.expect_number()? as u32;
37530                    let s = if self.match_token(TokenType::Comma) {
37531                        Some(self.expect_number()? as u32)
37532                    } else {
37533                        None
37534                    };
37535                    self.expect(TokenType::RParen)?;
37536                    (Some(p), s)
37537                } else {
37538                    (None, None)
37539                };
37540                Ok(DataType::Decimal { precision, scale })
37541            }
37542            "BOOLEAN" | "BOOL" => Ok(DataType::Boolean),
37543            "CHAR" | "CHARACTER" | "NCHAR" => {
37544                let is_nchar = name == "NCHAR";
37545                // SQL standard: CHARACTER LARGE OBJECT → CLOB/TEXT
37546                if self.match_identifier("LARGE") && self.match_identifier("OBJECT") {
37547                    return Ok(DataType::Text);
37548                }
37549                // Check for VARYING to convert to VARCHAR (SQL standard: CHAR VARYING, CHARACTER VARYING)
37550                if self.match_identifier("VARYING") {
37551                    let length = if self.match_token(TokenType::LParen) {
37552                        if self.check(TokenType::RParen) {
37553                            self.skip();
37554                            None
37555                        } else {
37556                            let n = self.expect_number()? as u32;
37557                            self.expect(TokenType::RParen)?;
37558                            Some(n)
37559                        }
37560                    } else {
37561                        None
37562                    };
37563                    Ok(DataType::VarChar {
37564                        length,
37565                        parenthesized_length: false,
37566                    })
37567                } else {
37568                    let length = if self.match_token(TokenType::LParen) {
37569                        // Allow empty parens like NCHAR() - treat as no length specified
37570                        if self.check(TokenType::RParen) {
37571                            self.skip(); // consume RParen
37572                            None
37573                        } else {
37574                            let n = self.expect_number()? as u32;
37575                            self.expect(TokenType::RParen)?;
37576                            Some(n)
37577                        }
37578                    } else {
37579                        None
37580                    };
37581                    // CHAR CHARACTER SET charset (MySQL CAST context, no length)
37582                    // When length is specified (e.g., CHAR(4) CHARACTER SET LATIN),
37583                    // CHARACTER SET is a column attribute handled at the column def level
37584                    if length.is_none()
37585                        && self.match_identifier("CHARACTER")
37586                        && self.match_token(TokenType::Set)
37587                    {
37588                        let charset = self.expect_identifier_or_keyword()?;
37589                        return Ok(DataType::CharacterSet { name: charset });
37590                    }
37591                    // Preserve NCHAR as Custom DataType so target dialects can map it properly
37592                    // (Oracle keeps NCHAR, TSQL keeps NCHAR, others map to CHAR)
37593                    if is_nchar {
37594                        let name = if let Some(len) = length {
37595                            format!("NCHAR({})", len)
37596                        } else {
37597                            "NCHAR".to_string()
37598                        };
37599                        return Ok(DataType::Custom { name });
37600                    }
37601                    Ok(DataType::Char { length })
37602                }
37603            }
37604            "VARCHAR" | "NVARCHAR" => {
37605                let is_nvarchar = name == "NVARCHAR";
37606                if self.match_token(TokenType::LParen) {
37607                    // Allow empty parens like NVARCHAR() - treat as no length specified
37608                    if self.check(TokenType::RParen) {
37609                        self.skip(); // consume RParen
37610                        if is_nvarchar {
37611                            return Ok(DataType::Custom {
37612                                name: "NVARCHAR".to_string(),
37613                            });
37614                        }
37615                        Ok(DataType::VarChar {
37616                            length: None,
37617                            parenthesized_length: false,
37618                        })
37619                    } else if self.check_identifier("MAX") {
37620                        // TSQL: VARCHAR(MAX) / NVARCHAR(MAX)
37621                        self.skip(); // consume MAX
37622                        self.expect(TokenType::RParen)?;
37623                        let type_name = if is_nvarchar {
37624                            "NVARCHAR(MAX)"
37625                        } else {
37626                            "VARCHAR(MAX)"
37627                        };
37628                        Ok(DataType::Custom {
37629                            name: type_name.to_string(),
37630                        })
37631                    } else {
37632                        // Hive allows VARCHAR((50)) - extra parentheses around the length
37633                        let parenthesized_length = self.match_token(TokenType::LParen);
37634                        let n = self.expect_number()? as u32;
37635                        if parenthesized_length {
37636                            self.expect(TokenType::RParen)?;
37637                        }
37638                        self.expect(TokenType::RParen)?;
37639                        // Preserve NVARCHAR as Custom DataType so target dialects can map properly
37640                        if is_nvarchar {
37641                            return Ok(DataType::Custom {
37642                                name: format!("NVARCHAR({})", n),
37643                            });
37644                        }
37645                        Ok(DataType::VarChar {
37646                            length: Some(n),
37647                            parenthesized_length,
37648                        })
37649                    }
37650                } else {
37651                    if is_nvarchar {
37652                        return Ok(DataType::Custom {
37653                            name: "NVARCHAR".to_string(),
37654                        });
37655                    }
37656                    Ok(DataType::VarChar {
37657                        length: None,
37658                        parenthesized_length: false,
37659                    })
37660                }
37661            }
37662            "TEXT" | "NTEXT" => {
37663                // TEXT(n) - optional length parameter
37664                if self.match_token(TokenType::LParen) {
37665                    let n = self.expect_number()? as u32;
37666                    self.expect(TokenType::RParen)?;
37667                    Ok(DataType::TextWithLength { length: n })
37668                } else {
37669                    Ok(DataType::Text)
37670                }
37671            }
37672            "STRING" => {
37673                // BigQuery STRING(n) - parameterized string with max length
37674                let length = if self.match_token(TokenType::LParen) {
37675                    let n = self.expect_number()? as u32;
37676                    self.expect(TokenType::RParen)?;
37677                    Some(n)
37678                } else {
37679                    None
37680                };
37681                Ok(DataType::String { length })
37682            }
37683            "DATE" => Ok(DataType::Date),
37684            "TIME" => {
37685                // ClickHouse: Time('timezone') is a custom type with string arg
37686                if matches!(
37687                    self.config.dialect,
37688                    Some(crate::dialects::DialectType::ClickHouse)
37689                ) && self.check(TokenType::LParen)
37690                    && self.current + 1 < self.tokens.len()
37691                    && self.tokens[self.current + 1].token_type == TokenType::String
37692                {
37693                    self.skip(); // consume LParen
37694                    let args = self.parse_custom_type_args_balanced()?;
37695                    self.expect(TokenType::RParen)?;
37696                    return Ok(DataType::Custom {
37697                        name: format!("Time({})", args),
37698                    });
37699                }
37700                let precision = if self.match_token(TokenType::LParen) {
37701                    if self.check(TokenType::RParen) {
37702                        self.skip();
37703                        None
37704                    } else {
37705                        let p = self.expect_number()? as u32;
37706                        self.expect(TokenType::RParen)?;
37707                        Some(p)
37708                    }
37709                } else {
37710                    None
37711                };
37712                // Handle TIME WITH/WITHOUT TIME ZONE
37713                let timezone = if self.match_token(TokenType::With) {
37714                    self.match_keyword("TIME");
37715                    self.match_keyword("ZONE");
37716                    true
37717                } else if self.match_keyword("WITHOUT") {
37718                    self.match_keyword("TIME");
37719                    self.match_keyword("ZONE");
37720                    false
37721                } else {
37722                    false
37723                };
37724                Ok(DataType::Time {
37725                    precision,
37726                    timezone,
37727                })
37728            }
37729            "TIMETZ" => {
37730                let precision = if self.match_token(TokenType::LParen) {
37731                    let p = self.expect_number()? as u32;
37732                    self.expect(TokenType::RParen)?;
37733                    Some(p)
37734                } else {
37735                    None
37736                };
37737                Ok(DataType::Time {
37738                    precision,
37739                    timezone: true,
37740                })
37741            }
37742            "TIMESTAMP" => {
37743                // Parse optional precision: TIMESTAMP(p)
37744                let precision = if self.match_token(TokenType::LParen) {
37745                    let p = self.expect_number()? as u32;
37746                    self.expect(TokenType::RParen)?;
37747                    Some(p)
37748                } else {
37749                    None
37750                };
37751                // Parse optional WITH/WITHOUT TIME ZONE or WITH LOCAL TIME ZONE
37752                // Note: TIME is a keyword (TokenType::Time) and LOCAL is a keyword (TokenType::Local)
37753                if self.match_token(TokenType::With) {
37754                    // Check for LOCAL TIME ZONE (Exasol) vs TIME ZONE
37755                    // LOCAL is tokenized as TokenType::Local, not as Identifier
37756                    if self.match_token(TokenType::Local) {
37757                        self.match_keyword("TIME");
37758                        self.match_keyword("ZONE");
37759                        // TIMESTAMP WITH LOCAL TIME ZONE - return as custom type for Exasol handling
37760                        Ok(DataType::Custom {
37761                            name: "TIMESTAMPLTZ".to_string(),
37762                        })
37763                    } else {
37764                        self.match_keyword("TIME");
37765                        self.match_keyword("ZONE");
37766                        Ok(DataType::Timestamp {
37767                            precision,
37768                            timezone: true,
37769                        })
37770                    }
37771                } else if self.match_keyword("WITHOUT") {
37772                    self.match_keyword("TIME");
37773                    self.match_keyword("ZONE");
37774                    Ok(DataType::Timestamp {
37775                        precision,
37776                        timezone: false,
37777                    })
37778                } else {
37779                    Ok(DataType::Timestamp {
37780                        precision,
37781                        timezone: false,
37782                    })
37783                }
37784            }
37785            "TIMESTAMPTZ" => {
37786                let precision = if self.match_token(TokenType::LParen) {
37787                    let p = self.expect_number()? as u32;
37788                    self.expect(TokenType::RParen)?;
37789                    Some(p)
37790                } else {
37791                    None
37792                };
37793                Ok(DataType::Timestamp {
37794                    precision,
37795                    timezone: true,
37796                })
37797            }
37798            "TIMESTAMPLTZ" | "TIMESTAMP_LTZ" => {
37799                let precision = if self.match_token(TokenType::LParen) {
37800                    let p = self.expect_number()? as u32;
37801                    self.expect(TokenType::RParen)?;
37802                    Some(p)
37803                } else {
37804                    None
37805                };
37806                let name = if let Some(p) = precision {
37807                    format!("TIMESTAMPLTZ({})", p)
37808                } else {
37809                    "TIMESTAMPLTZ".to_string()
37810                };
37811                Ok(DataType::Custom { name })
37812            }
37813            "INTERVAL" => {
37814                // Parse optional unit (DAYS, DAY, HOUR, etc.)
37815                // Don't consume GENERATED, AS, NOT, NULL, etc. which are column constraints
37816                let unit = if (self.check(TokenType::Identifier)
37817                    || self.check(TokenType::Var)
37818                    || self.check_keyword())
37819                    && !self.check(TokenType::Generated)
37820                    && !self.check(TokenType::As)
37821                    && !self.check(TokenType::Not)
37822                    && !self.check(TokenType::Null)
37823                    && !self.check(TokenType::Default)
37824                    && !self.check(TokenType::PrimaryKey)
37825                    && !self.check(TokenType::Unique)
37826                    && !self.check(TokenType::Check)
37827                    && !self.check(TokenType::Constraint)
37828                    && !self.check(TokenType::References)
37829                    && !self.check(TokenType::Collate)
37830                    && !self.check(TokenType::Comment)
37831                    && !self.check(TokenType::RParen)
37832                    && !self.check(TokenType::Comma)
37833                {
37834                    Some(self.advance().text.to_ascii_uppercase())
37835                } else {
37836                    None
37837                };
37838                // Parse optional TO unit for range intervals like DAY TO HOUR
37839                let to = if self.match_token(TokenType::To) {
37840                    if self.check(TokenType::Identifier)
37841                        || self.check(TokenType::Var)
37842                        || self.check_keyword()
37843                    {
37844                        Some(self.advance().text.to_ascii_uppercase())
37845                    } else {
37846                        None
37847                    }
37848                } else {
37849                    None
37850                };
37851                Ok(DataType::Interval { unit, to })
37852            }
37853            "JSON" => {
37854                if matches!(
37855                    self.config.dialect,
37856                    Some(crate::dialects::DialectType::ClickHouse)
37857                ) && self.match_token(TokenType::LParen)
37858                {
37859                    // ClickHouse: JSON(subcolumn_specs) e.g. JSON(a String, b UInt32) or JSON(max_dynamic_paths=8)
37860                    let args = self.parse_custom_type_args_balanced()?;
37861                    self.expect(TokenType::RParen)?;
37862                    // Uppercase the SKIP keyword in JSON type declarations
37863                    // e.g., "col1 String, skip col2" -> "col1 String, SKIP col2"
37864                    let args = Self::uppercase_json_type_skip_keyword(&args);
37865                    Ok(DataType::Custom {
37866                        name: format!("JSON({})", args),
37867                    })
37868                } else {
37869                    Ok(DataType::Json)
37870                }
37871            }
37872            "JSONB" => Ok(DataType::JsonB),
37873            "UUID" => Ok(DataType::Uuid),
37874            "BLOB" => Ok(DataType::Blob),
37875            "BYTEA" => Ok(DataType::VarBinary { length: None }),
37876            "BIT" => {
37877                let length = if self.match_token(TokenType::LParen) {
37878                    let n = self.expect_number()? as u32;
37879                    self.expect(TokenType::RParen)?;
37880                    Some(n)
37881                } else {
37882                    None
37883                };
37884                Ok(DataType::Bit { length })
37885            }
37886            "VARBIT" | "BIT VARYING" => {
37887                let length = if self.match_token(TokenType::LParen) {
37888                    let n = self.expect_number()? as u32;
37889                    self.expect(TokenType::RParen)?;
37890                    Some(n)
37891                } else {
37892                    None
37893                };
37894                Ok(DataType::VarBit { length })
37895            }
37896            "BINARY" => {
37897                // SQL standard: BINARY LARGE OBJECT → BLOB
37898                if self.match_identifier("LARGE") && self.match_identifier("OBJECT") {
37899                    return Ok(DataType::Blob);
37900                }
37901                // Handle BINARY VARYING (SQL standard for VARBINARY)
37902                if self.match_identifier("VARYING") {
37903                    let length = if self.match_token(TokenType::LParen) {
37904                        let len = self.expect_number()? as u32;
37905                        self.expect(TokenType::RParen)?;
37906                        Some(len)
37907                    } else {
37908                        None
37909                    };
37910                    Ok(DataType::VarBinary { length })
37911                } else {
37912                    let length = if self.match_token(TokenType::LParen) {
37913                        let len = self.expect_number()? as u32;
37914                        self.expect(TokenType::RParen)?;
37915                        Some(len)
37916                    } else {
37917                        None
37918                    };
37919                    Ok(DataType::Binary { length })
37920                }
37921            }
37922            "VARBINARY" => {
37923                let length = if self.match_token(TokenType::LParen) {
37924                    let len = self.expect_number()? as u32;
37925                    self.expect(TokenType::RParen)?;
37926                    Some(len)
37927                } else {
37928                    None
37929                };
37930                Ok(DataType::VarBinary { length })
37931            }
37932            // Generic types with angle bracket or parentheses syntax: ARRAY<T>, ARRAY(T), MAP<K,V>, MAP(K,V)
37933            "ARRAY" => {
37934                if self.match_token(TokenType::Lt) {
37935                    // ARRAY<element_type> - angle bracket style
37936                    let element_type = self.parse_data_type()?;
37937                    self.expect_gt()?;
37938                    Ok(DataType::Array {
37939                        element_type: Box::new(element_type),
37940                        dimension: None,
37941                    })
37942                } else if self.match_token(TokenType::LParen) {
37943                    // ARRAY(element_type) - Snowflake parentheses style
37944                    let element_type = self.parse_data_type()?;
37945                    self.expect(TokenType::RParen)?;
37946                    Ok(DataType::Array {
37947                        element_type: Box::new(element_type),
37948                        dimension: None,
37949                    })
37950                } else {
37951                    // Just ARRAY without type parameter
37952                    Ok(DataType::Custom {
37953                        name: "ARRAY".to_string(),
37954                    })
37955                }
37956            }
37957            "MAP" => {
37958                if self.match_token(TokenType::Lt) {
37959                    // MAP<key_type, value_type> - angle bracket style
37960                    let key_type = self.parse_data_type()?;
37961                    self.expect(TokenType::Comma)?;
37962                    let value_type = self.parse_data_type()?;
37963                    self.expect_gt()?;
37964                    Ok(DataType::Map {
37965                        key_type: Box::new(key_type),
37966                        value_type: Box::new(value_type),
37967                    })
37968                } else if self.match_token(TokenType::LBracket) {
37969                    // Materialize: MAP[TEXT => INT] type syntax
37970                    let key_type = self.parse_data_type()?;
37971                    self.expect(TokenType::FArrow)?;
37972                    let value_type = self.parse_data_type()?;
37973                    self.expect(TokenType::RBracket)?;
37974                    Ok(DataType::Map {
37975                        key_type: Box::new(key_type),
37976                        value_type: Box::new(value_type),
37977                    })
37978                } else if self.match_token(TokenType::LParen) {
37979                    // MAP(key_type, value_type) - Snowflake parentheses style
37980                    let key_type = self.parse_data_type()?;
37981                    self.expect(TokenType::Comma)?;
37982                    let value_type = self.parse_data_type()?;
37983                    self.expect(TokenType::RParen)?;
37984                    Ok(DataType::Map {
37985                        key_type: Box::new(key_type),
37986                        value_type: Box::new(value_type),
37987                    })
37988                } else {
37989                    // Just MAP without type parameters
37990                    Ok(DataType::Custom {
37991                        name: "MAP".to_string(),
37992                    })
37993                }
37994            }
37995            // VECTOR(type, dimension) - Snowflake vector type
37996            // VECTOR(dimension, element_type_alias) or VECTOR(dimension) - SingleStore vector type
37997            "VECTOR" => {
37998                if self.match_token(TokenType::LParen) {
37999                    if self.check(TokenType::Number) {
38000                        // SingleStore format: VECTOR(dimension) or VECTOR(dimension, type_alias)
38001                        let dimension = self.expect_number()? as u32;
38002                        let element_type = if self.match_token(TokenType::Comma) {
38003                            // Parse the type alias (I8, I16, I32, I64, F32, F64)
38004                            let type_alias = self.expect_identifier_or_keyword()?;
38005                            let mapped_type = match type_alias.to_ascii_uppercase().as_str() {
38006                                "I8" => DataType::TinyInt { length: None },
38007                                "I16" => DataType::SmallInt { length: None },
38008                                "I32" => DataType::Int {
38009                                    length: None,
38010                                    integer_spelling: false,
38011                                },
38012                                "I64" => DataType::BigInt { length: None },
38013                                "F32" => DataType::Float {
38014                                    precision: None,
38015                                    scale: None,
38016                                    real_spelling: false,
38017                                },
38018                                "F64" => DataType::Double {
38019                                    precision: None,
38020                                    scale: None,
38021                                },
38022                                _ => DataType::Custom {
38023                                    name: type_alias.to_string(),
38024                                },
38025                            };
38026                            Some(Box::new(mapped_type))
38027                        } else {
38028                            // Just dimension, no type
38029                            None
38030                        };
38031                        self.expect(TokenType::RParen)?;
38032                        Ok(DataType::Vector {
38033                            element_type,
38034                            dimension: Some(dimension),
38035                        })
38036                    } else {
38037                        // Snowflake format: VECTOR(type, dimension)
38038                        let element_type = self.parse_data_type()?;
38039                        self.expect(TokenType::Comma)?;
38040                        let dimension = self.expect_number()? as u32;
38041                        self.expect(TokenType::RParen)?;
38042                        Ok(DataType::Vector {
38043                            element_type: Some(Box::new(element_type)),
38044                            dimension: Some(dimension),
38045                        })
38046                    }
38047                } else {
38048                    Ok(DataType::Custom {
38049                        name: "VECTOR".to_string(),
38050                    })
38051                }
38052            }
38053            // OBJECT(field1 type1, field2 type2, ...) - Snowflake structured object type
38054            "OBJECT" => {
38055                if self.match_token(TokenType::LParen) {
38056                    // ClickHouse: Object('json') — string literal argument
38057                    if matches!(
38058                        self.config.dialect,
38059                        Some(crate::dialects::DialectType::ClickHouse)
38060                    ) && self.check(TokenType::String)
38061                    {
38062                        let arg = self.advance().text;
38063                        self.expect(TokenType::RParen)?;
38064                        return Ok(DataType::Custom {
38065                            name: format!("Object('{}')", arg),
38066                        });
38067                    }
38068                    let mut fields = Vec::new();
38069                    if !self.check(TokenType::RParen) {
38070                        loop {
38071                            let field_name = self.expect_identifier_or_keyword()?;
38072                            let field_type = self.parse_data_type()?;
38073                            // Optional NOT NULL constraint
38074                            let not_null = if self.match_keyword("NOT") {
38075                                // Consume NULL if present
38076                                self.match_keyword("NULL");
38077                                true
38078                            } else {
38079                                false
38080                            };
38081                            fields.push((field_name, field_type, not_null));
38082                            if !self.match_token(TokenType::Comma) {
38083                                break;
38084                            }
38085                        }
38086                    }
38087                    self.expect(TokenType::RParen)?;
38088                    // Check for RENAME FIELDS or ADD FIELDS modifier
38089                    let modifier = if self.match_keyword("RENAME") {
38090                        if self.match_keyword("FIELDS") {
38091                            Some("RENAME FIELDS".to_string())
38092                        } else {
38093                            Some("RENAME".to_string())
38094                        }
38095                    } else if self.match_keyword("ADD") {
38096                        if self.match_keyword("FIELDS") {
38097                            Some("ADD FIELDS".to_string())
38098                        } else {
38099                            Some("ADD".to_string())
38100                        }
38101                    } else {
38102                        None
38103                    };
38104                    Ok(DataType::Object { fields, modifier })
38105                } else {
38106                    Ok(DataType::Custom {
38107                        name: "OBJECT".to_string(),
38108                    })
38109                }
38110            }
38111            "STRUCT" => {
38112                if self.match_token(TokenType::Lt) {
38113                    // STRUCT<field1 type1, field2 type2, ...> - BigQuery angle-bracket syntax
38114                    let fields = self.parse_struct_type_fields(false)?;
38115                    self.expect_gt()?;
38116                    Ok(DataType::Struct {
38117                        fields,
38118                        nested: false,
38119                    })
38120                } else if self.match_token(TokenType::LParen) {
38121                    // STRUCT(field1 type1, field2 type2, ...) - DuckDB parenthesized syntax
38122                    let fields = self.parse_struct_type_fields(true)?;
38123                    self.expect(TokenType::RParen)?;
38124                    Ok(DataType::Struct {
38125                        fields,
38126                        nested: true,
38127                    })
38128                } else {
38129                    // Just STRUCT without type parameters
38130                    Ok(DataType::Custom {
38131                        name: "STRUCT".to_string(),
38132                    })
38133                }
38134            }
38135            "ROW" => {
38136                // ROW(field1 type1, field2 type2, ...) - same as STRUCT with parens
38137                if self.match_token(TokenType::LParen) {
38138                    let fields = self.parse_struct_type_fields(true)?;
38139                    self.expect(TokenType::RParen)?;
38140                    Ok(DataType::Struct {
38141                        fields,
38142                        nested: true,
38143                    })
38144                } else {
38145                    Ok(DataType::Custom {
38146                        name: "ROW".to_string(),
38147                    })
38148                }
38149            }
38150            "RECORD" => {
38151                // RECORD(field1 type1, field2 type2, ...) - SingleStore record type (like ROW/STRUCT)
38152                if self.match_token(TokenType::LParen) {
38153                    let fields = self.parse_struct_type_fields(true)?;
38154                    self.expect(TokenType::RParen)?;
38155                    // Use Struct with nested=true, generator will output RECORD for SingleStore
38156                    Ok(DataType::Struct {
38157                        fields,
38158                        nested: true,
38159                    })
38160                } else {
38161                    Ok(DataType::Custom {
38162                        name: "RECORD".to_string(),
38163                    })
38164                }
38165            }
38166            "ENUM" => {
38167                // ENUM('RED', 'GREEN', 'BLUE') - DuckDB enum type
38168                // ClickHouse: Enum('hello' = 1, 'world' = 2)
38169                // ClickHouse also allows NULL in enum: Enum('a', 'b', NULL)
38170                if self.match_token(TokenType::LParen) {
38171                    let mut values = Vec::new();
38172                    let mut assignments = Vec::new();
38173                    if !self.check(TokenType::RParen) {
38174                        loop {
38175                            let val = if matches!(
38176                                self.config.dialect,
38177                                Some(crate::dialects::DialectType::ClickHouse)
38178                            ) && self.check(TokenType::Null)
38179                            {
38180                                self.skip();
38181                                "NULL".to_string()
38182                            } else {
38183                                self.expect_string()?
38184                            };
38185                            values.push(val);
38186                            // ClickHouse: optional = value assignment (including negative numbers)
38187                            if self.match_token(TokenType::Eq) {
38188                                let negative = self.match_token(TokenType::Dash);
38189                                let num_token = self.advance();
38190                                let val = if negative {
38191                                    format!("-{}", num_token.text)
38192                                } else {
38193                                    num_token.text.clone()
38194                                };
38195                                assignments.push(Some(val));
38196                            } else {
38197                                assignments.push(None);
38198                            }
38199                            if !self.match_token(TokenType::Comma) {
38200                                break;
38201                            }
38202                        }
38203                    }
38204                    self.expect(TokenType::RParen)?;
38205                    Ok(DataType::Enum {
38206                        values,
38207                        assignments,
38208                    })
38209                } else {
38210                    Ok(DataType::Custom {
38211                        name: "ENUM".to_string(),
38212                    })
38213                }
38214            }
38215            "SET" => {
38216                // MySQL SET('a', 'b', 'c') type
38217                if self.match_token(TokenType::LParen) {
38218                    let mut values = Vec::new();
38219                    if !self.check(TokenType::RParen) {
38220                        loop {
38221                            let val = self.expect_string()?;
38222                            values.push(val);
38223                            if !self.match_token(TokenType::Comma) {
38224                                break;
38225                            }
38226                        }
38227                    }
38228                    self.expect(TokenType::RParen)?;
38229                    Ok(DataType::Set { values })
38230                } else {
38231                    Ok(DataType::Custom {
38232                        name: "SET".to_string(),
38233                    })
38234                }
38235            }
38236            "UNION" if self.check(TokenType::LParen) => {
38237                // UNION(num INT, str TEXT) - DuckDB union type (only when followed by paren)
38238                self.skip(); // consume LParen
38239                let struct_fields = self.parse_struct_type_fields(true)?;
38240                self.expect(TokenType::RParen)?;
38241                // Convert StructField to (String, DataType) for Union
38242                let fields: Vec<(String, DataType)> = struct_fields
38243                    .into_iter()
38244                    .map(|f| (f.name, f.data_type))
38245                    .collect();
38246                Ok(DataType::Union { fields })
38247            }
38248            // Spatial types
38249            "GEOMETRY" => {
38250                let (subtype, srid) = self.parse_spatial_type_args()?;
38251                Ok(DataType::Geometry { subtype, srid })
38252            }
38253            "GEOGRAPHY" => {
38254                let (subtype, srid) = self.parse_spatial_type_args()?;
38255                Ok(DataType::Geography { subtype, srid })
38256            }
38257            // MySQL spatial subtypes without wrapper
38258            "POINT" | "LINESTRING" | "POLYGON" | "MULTIPOINT" | "MULTILINESTRING"
38259            | "MULTIPOLYGON" | "GEOMETRYCOLLECTION" => {
38260                // Check for optional SRID clause (MySQL syntax)
38261                let srid = if self.match_identifier("SRID") {
38262                    Some(self.expect_number()? as u32)
38263                } else {
38264                    None
38265                };
38266                Ok(DataType::Geometry {
38267                    subtype: Some(name),
38268                    srid,
38269                })
38270            }
38271            // BigQuery ANY TYPE - templated parameter type for UDFs
38272            "ANY" => {
38273                if self.match_token(TokenType::Type) {
38274                    Ok(DataType::Custom {
38275                        name: "ANY TYPE".to_string(),
38276                    })
38277                } else {
38278                    Ok(DataType::Custom {
38279                        name: "ANY".to_string(),
38280                    })
38281                }
38282            }
38283            // LONG VARCHAR (Exasol) - same as TEXT
38284            "LONG" => {
38285                if self.match_identifier("VARCHAR") {
38286                    Ok(DataType::Text)
38287                } else {
38288                    Ok(DataType::Custom {
38289                        name: "LONG".to_string(),
38290                    })
38291                }
38292            }
38293            // MySQL SIGNED [INTEGER] / UNSIGNED [INTEGER] in CAST context
38294            // CAST(x AS SIGNED INTEGER) -> CAST(x AS SIGNED)
38295            "SIGNED" | "UNSIGNED" => {
38296                // Consume optional INTEGER keyword after SIGNED/UNSIGNED
38297                if self.check_identifier("INTEGER")
38298                    || self.check_keyword_text("INTEGER")
38299                    || self.check_keyword_text("INT")
38300                {
38301                    self.skip();
38302                }
38303                Ok(DataType::Custom { name })
38304            }
38305            // ClickHouse Nullable(T) wrapper type
38306            "NULLABLE" => {
38307                self.expect(TokenType::LParen)?;
38308                let inner = self.parse_data_type()?;
38309                self.expect(TokenType::RParen)?;
38310                Ok(DataType::Nullable {
38311                    inner: Box::new(inner),
38312                })
38313            }
38314            _ => {
38315                // Handle custom types with optional parenthesized precision/args
38316                // e.g., DATETIME2(2), DATETIMEOFFSET(7), NVARCHAR2(100)
38317                // Use uppercase name for known SQL custom types, but preserve original case
38318                // for user-defined type names (e.g., UserDefinedTableType)
38319                let is_known = convert_name_is_known_custom(&name);
38320                let custom_name = if is_known {
38321                    name.clone()
38322                } else {
38323                    raw_name.clone()
38324                };
38325                if self.match_token(TokenType::LParen) {
38326                    if matches!(
38327                        self.config.dialect,
38328                        Some(crate::dialects::DialectType::ClickHouse)
38329                    ) {
38330                        let args = self.parse_custom_type_args_balanced()?;
38331                        self.expect(TokenType::RParen)?;
38332                        Ok(DataType::Custom {
38333                            name: format!("{}({})", custom_name, args),
38334                        })
38335                    } else {
38336                        let mut args = Vec::new();
38337                        let mut after_comma = true; // treat first token as start of new arg
38338                        loop {
38339                            if self.check(TokenType::RParen) {
38340                                break;
38341                            }
38342                            let token = self.advance();
38343                            // If the previous token was space-separated (not comma-separated),
38344                            // append to the last arg. E.g., VARCHAR2(2328 CHAR) -> "2328 CHAR"
38345                            if !after_comma && !args.is_empty() {
38346                                if let Some(last) = args.last_mut() {
38347                                    *last = format!("{} {}", last, token.text);
38348                                }
38349                            } else {
38350                                args.push(token.text.clone());
38351                            }
38352                            after_comma = self.match_token(TokenType::Comma);
38353                        }
38354                        self.expect(TokenType::RParen)?;
38355                        // Include args in the name: DATETIME2(2), VARCHAR2(2328 CHAR)
38356                        Ok(DataType::Custom {
38357                            name: format!("{}({})", custom_name, args.join(", ")),
38358                        })
38359                    }
38360                } else {
38361                    Ok(DataType::Custom { name: custom_name })
38362                }
38363            }
38364        }?;
38365
38366        // UNSIGNED/SIGNED modifiers for integer types (MySQL) are handled
38367        // by the column definition parser which sets col.unsigned = true.
38368        // Do NOT consume them here; the column parser needs to see them.
38369        let mut result_type = base_type;
38370
38371        // Materialize: handle postfix LIST syntax (INT LIST, INT LIST LIST LIST)
38372        let is_materialize = matches!(
38373            self.config.dialect,
38374            Some(crate::dialects::DialectType::Materialize)
38375        );
38376        if is_materialize {
38377            while self.check_identifier("LIST") || self.check(TokenType::List) {
38378                self.skip(); // consume LIST
38379                result_type = DataType::List {
38380                    element_type: Box::new(result_type),
38381                };
38382            }
38383        }
38384
38385        // PostgreSQL array syntax: TYPE[], TYPE[N], TYPE[N][M], etc.
38386        let result_type = self.maybe_parse_array_dimensions(result_type)?;
38387
38388        // ClickHouse: mark string-like standard types as non-nullable by converting to Custom
38389        // This prevents the generator from wrapping them in Nullable() during identity transforms.
38390        // Types parsed from other dialects remain standard and will get Nullable wrapping when
38391        // transpiling to ClickHouse.
38392        if matches!(
38393            self.config.dialect,
38394            Some(crate::dialects::DialectType::ClickHouse)
38395        ) {
38396            return Ok(Self::clickhouse_mark_non_nullable(result_type));
38397        }
38398
38399        Ok(result_type)
38400    }
38401
38402    /// Convert standard types to Custom equivalents for ClickHouse to prevent Nullable wrapping.
38403    /// This mirrors Python sqlglot's behavior of marking ClickHouse-parsed types as non-nullable.
38404    fn clickhouse_mark_non_nullable(dt: DataType) -> DataType {
38405        match dt {
38406            DataType::Text => DataType::Custom {
38407                name: "String".to_string(),
38408            },
38409            DataType::VarChar { .. } => DataType::Custom {
38410                name: "String".to_string(),
38411            },
38412            DataType::Char { .. } => DataType::Custom {
38413                name: "String".to_string(),
38414            },
38415            DataType::String { .. } => DataType::Custom {
38416                name: "String".to_string(),
38417            },
38418            _ => dt,
38419        }
38420    }
38421
38422    /// Parse a data type for cast syntax (::TYPE)
38423    /// For dialects that support fixed-size arrays (like DuckDB), brackets like [3] are
38424    /// parsed as array dimensions (e.g., x::INT[3] means cast to INT[3] array type).
38425    /// For other dialects (like Snowflake), brackets are subscript operations
38426    /// (e.g., x::VARIANT[0] means cast to VARIANT, then subscript with [0]).
38427    fn parse_data_type_for_cast(&mut self) -> Result<DataType> {
38428        // Check if dialect supports array type suffixes (e.g., INT[], VARCHAR[3])
38429        // PostgreSQL: INT[], TEXT[] (no fixed size)
38430        // DuckDB: INT[3] (fixed size arrays)
38431        let supports_array_type_suffix = matches!(
38432            self.config.dialect,
38433            Some(crate::dialects::DialectType::DuckDB)
38434                | Some(crate::dialects::DialectType::PostgreSQL)
38435                | Some(crate::dialects::DialectType::Redshift)
38436        );
38437
38438        // Check if it's a quoted identifier (e.g., "udt") — preserve case and quoting
38439        let is_quoted = self.check(TokenType::QuotedIdentifier);
38440        let raw_name = self.expect_identifier_or_keyword()?;
38441        if is_quoted {
38442            // Check if the quoted name matches a known type — if so, normalize it
38443            let known_type = self.convert_name_to_type(&raw_name);
38444            if let Ok(ref dt) = known_type {
38445                if !matches!(dt, DataType::Custom { .. }) {
38446                    return known_type;
38447                }
38448            }
38449            // Truly custom type — preserve original case with quotes
38450            return Ok(DataType::Custom {
38451                name: format!("\"{}\"", raw_name),
38452            });
38453        }
38454        let name = raw_name.to_ascii_uppercase();
38455
38456        // Handle parametric types like ARRAY<T>, MAP<K,V>
38457        let base_type = match name.as_str() {
38458            "ARRAY" => {
38459                if self.match_token(TokenType::Lt) {
38460                    let element_type = self.parse_data_type()?;
38461                    self.expect_gt()?;
38462                    DataType::Array {
38463                        element_type: Box::new(element_type),
38464                        dimension: None,
38465                    }
38466                } else if self.match_token(TokenType::LParen) {
38467                    // ClickHouse: Array(Type) syntax with parentheses
38468                    let element_type = self.parse_data_type_for_cast()?;
38469                    self.expect(TokenType::RParen)?;
38470                    DataType::Array {
38471                        element_type: Box::new(element_type),
38472                        dimension: None,
38473                    }
38474                } else {
38475                    DataType::Custom { name }
38476                }
38477            }
38478            "MAP" => {
38479                if self.match_token(TokenType::Lt) {
38480                    let key_type = self.parse_data_type()?;
38481                    self.expect(TokenType::Comma)?;
38482                    let value_type = self.parse_data_type()?;
38483                    self.expect_gt()?;
38484                    DataType::Map {
38485                        key_type: Box::new(key_type),
38486                        value_type: Box::new(value_type),
38487                    }
38488                } else if self.match_token(TokenType::LParen) {
38489                    // Snowflake: MAP(key_type, value_type) syntax
38490                    let key_type = self.parse_data_type_for_cast()?;
38491                    self.expect(TokenType::Comma)?;
38492                    let value_type = self.parse_data_type_for_cast()?;
38493                    self.expect(TokenType::RParen)?;
38494                    DataType::Map {
38495                        key_type: Box::new(key_type),
38496                        value_type: Box::new(value_type),
38497                    }
38498                } else if self.match_token(TokenType::LBracket) {
38499                    // Materialize: MAP[TEXT => INT] type syntax
38500                    let key_type = self.parse_data_type_for_cast()?;
38501                    self.expect(TokenType::FArrow)?;
38502                    let value_type = self.parse_data_type_for_cast()?;
38503                    self.expect(TokenType::RBracket)?;
38504                    DataType::Map {
38505                        key_type: Box::new(key_type),
38506                        value_type: Box::new(value_type),
38507                    }
38508                } else {
38509                    DataType::Custom { name }
38510                }
38511            }
38512            "STRUCT" => {
38513                if self.match_token(TokenType::Lt) {
38514                    let fields = self.parse_struct_type_fields(false)?;
38515                    self.expect_gt()?;
38516                    DataType::Struct {
38517                        fields,
38518                        nested: false,
38519                    }
38520                } else if self.match_token(TokenType::LParen) {
38521                    let fields = self.parse_struct_type_fields(true)?;
38522                    self.expect(TokenType::RParen)?;
38523                    DataType::Struct {
38524                        fields,
38525                        nested: true,
38526                    }
38527                } else {
38528                    DataType::Custom { name }
38529                }
38530            }
38531            "ROW" => {
38532                if self.match_token(TokenType::LParen) {
38533                    let fields = self.parse_struct_type_fields(true)?;
38534                    self.expect(TokenType::RParen)?;
38535                    DataType::Struct {
38536                        fields,
38537                        nested: true,
38538                    }
38539                } else {
38540                    DataType::Custom { name }
38541                }
38542            }
38543            "RECORD" => {
38544                // SingleStore RECORD type (like ROW/STRUCT)
38545                if self.match_token(TokenType::LParen) {
38546                    let fields = self.parse_struct_type_fields(true)?;
38547                    self.expect(TokenType::RParen)?;
38548                    DataType::Struct {
38549                        fields,
38550                        nested: true,
38551                    }
38552                } else {
38553                    DataType::Custom { name }
38554                }
38555            }
38556            // Multi-word types that need special handling in cast context
38557            "DOUBLE" => {
38558                // Handle DOUBLE PRECISION
38559                let _ = self.match_identifier("PRECISION");
38560                // ClickHouse/SQL: DOUBLE(precision) or DOUBLE(precision, scale)
38561                let (precision, scale) = if self.match_token(TokenType::LParen) {
38562                    let p = Some(self.expect_number()? as u32);
38563                    let s = if self.match_token(TokenType::Comma) {
38564                        Some(self.expect_number()? as u32)
38565                    } else {
38566                        None
38567                    };
38568                    self.expect(TokenType::RParen)?;
38569                    (p, s)
38570                } else {
38571                    (None, None)
38572                };
38573                DataType::Double { precision, scale }
38574            }
38575            "CHARACTER" | "CHAR" | "NCHAR" => {
38576                // Handle CHARACTER VARYING / CHAR VARYING
38577                if self.match_identifier("VARYING") {
38578                    let length = if self.match_token(TokenType::LParen) {
38579                        let len = Some(self.expect_number()? as u32);
38580                        self.expect(TokenType::RParen)?;
38581                        len
38582                    } else {
38583                        None
38584                    };
38585                    DataType::VarChar {
38586                        length,
38587                        parenthesized_length: false,
38588                    }
38589                } else {
38590                    let length = if self.match_token(TokenType::LParen) {
38591                        let len = Some(self.expect_number()? as u32);
38592                        self.expect(TokenType::RParen)?;
38593                        len
38594                    } else {
38595                        None
38596                    };
38597                    // CHAR CHARACTER SET charset (MySQL CAST context, no length)
38598                    if length.is_none()
38599                        && self.match_identifier("CHARACTER")
38600                        && self.match_token(TokenType::Set)
38601                    {
38602                        let charset = self.expect_identifier_or_keyword()?;
38603                        return Ok(DataType::CharacterSet { name: charset });
38604                    }
38605                    DataType::Char { length }
38606                }
38607            }
38608            "TIME" => {
38609                // Handle TIME(precision) WITH/WITHOUT TIME ZONE
38610                let precision = if self.match_token(TokenType::LParen) {
38611                    let p = Some(self.expect_number()? as u32);
38612                    self.expect(TokenType::RParen)?;
38613                    p
38614                } else {
38615                    None
38616                };
38617                let timezone = if self.match_token(TokenType::With) {
38618                    self.match_keyword("TIME");
38619                    self.match_keyword("ZONE");
38620                    true
38621                } else if self.match_keyword("WITHOUT") {
38622                    self.match_keyword("TIME");
38623                    self.match_keyword("ZONE");
38624                    false
38625                } else {
38626                    false
38627                };
38628                DataType::Time {
38629                    precision,
38630                    timezone,
38631                }
38632            }
38633            "TIMETZ" => {
38634                let precision = if self.match_token(TokenType::LParen) {
38635                    let p = Some(self.expect_number()? as u32);
38636                    self.expect(TokenType::RParen)?;
38637                    p
38638                } else {
38639                    None
38640                };
38641                DataType::Time {
38642                    precision,
38643                    timezone: true,
38644                }
38645            }
38646            "TIMESTAMP" => {
38647                // Handle TIMESTAMP(precision) WITH/WITHOUT TIME ZONE or WITH LOCAL TIME ZONE
38648                let precision = if self.match_token(TokenType::LParen) {
38649                    let p = Some(self.expect_number()? as u32);
38650                    self.expect(TokenType::RParen)?;
38651                    p
38652                } else {
38653                    None
38654                };
38655                // Note: TIME is a keyword (TokenType::Time), so use match_keyword instead of match_identifier
38656                if self.match_token(TokenType::With) {
38657                    // Check for LOCAL TIME ZONE vs TIME ZONE
38658                    if self.match_token(TokenType::Local) {
38659                        self.match_keyword("TIME");
38660                        self.match_keyword("ZONE");
38661                        // TIMESTAMP WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
38662                        DataType::Custom {
38663                            name: "TIMESTAMPLTZ".to_string(),
38664                        }
38665                    } else {
38666                        self.match_keyword("TIME");
38667                        self.match_keyword("ZONE");
38668                        DataType::Timestamp {
38669                            precision,
38670                            timezone: true,
38671                        }
38672                    }
38673                } else if self.match_keyword("WITHOUT") {
38674                    self.match_keyword("TIME");
38675                    self.match_keyword("ZONE");
38676                    DataType::Timestamp {
38677                        precision,
38678                        timezone: false,
38679                    }
38680                } else {
38681                    DataType::Timestamp {
38682                        precision,
38683                        timezone: false,
38684                    }
38685                }
38686            }
38687            "TIMESTAMPTZ" => {
38688                let precision = if self.match_token(TokenType::LParen) {
38689                    let p = self.expect_number()? as u32;
38690                    self.expect(TokenType::RParen)?;
38691                    Some(p)
38692                } else {
38693                    None
38694                };
38695                DataType::Timestamp {
38696                    precision,
38697                    timezone: true,
38698                }
38699            }
38700            "TIMESTAMPLTZ" | "TIMESTAMP_LTZ" => {
38701                let precision = if self.match_token(TokenType::LParen) {
38702                    let p = self.expect_number()? as u32;
38703                    self.expect(TokenType::RParen)?;
38704                    Some(p)
38705                } else {
38706                    None
38707                };
38708                let dt_name = if let Some(p) = precision {
38709                    format!("TIMESTAMPLTZ({})", p)
38710                } else {
38711                    "TIMESTAMPLTZ".to_string()
38712                };
38713                DataType::Custom { name: dt_name }
38714            }
38715            "INTERVAL" => {
38716                // Parse optional unit (DAY, HOUR, etc.) after INTERVAL in cast context
38717                let unit = if (self.check(TokenType::Identifier)
38718                    || self.check(TokenType::Var)
38719                    || self.check_keyword())
38720                    && !self.check(TokenType::RParen)
38721                    && !self.check(TokenType::Comma)
38722                    && !self.check(TokenType::As)
38723                    && !self.check(TokenType::Not)
38724                    && !self.check(TokenType::Null)
38725                {
38726                    Some(self.advance().text.to_ascii_uppercase())
38727                } else {
38728                    None
38729                };
38730                // Parse optional TO unit for range intervals like DAY TO HOUR
38731                let to = if self.match_token(TokenType::To) {
38732                    if self.check(TokenType::Identifier)
38733                        || self.check(TokenType::Var)
38734                        || self.check_keyword()
38735                    {
38736                        Some(self.advance().text.to_ascii_uppercase())
38737                    } else {
38738                        None
38739                    }
38740                } else {
38741                    None
38742                };
38743                DataType::Interval { unit, to }
38744            }
38745            // VARCHAR/NVARCHAR with optional (N) or (MAX) parameter
38746            "VARCHAR" | "NVARCHAR" => {
38747                let is_nvarchar = name == "NVARCHAR";
38748                if self.match_token(TokenType::LParen) {
38749                    if self.check(TokenType::RParen) {
38750                        self.skip();
38751                        DataType::VarChar {
38752                            length: None,
38753                            parenthesized_length: false,
38754                        }
38755                    } else if self.check_identifier("MAX") {
38756                        self.skip();
38757                        self.expect(TokenType::RParen)?;
38758                        let type_name = if is_nvarchar {
38759                            "NVARCHAR(MAX)"
38760                        } else {
38761                            "VARCHAR(MAX)"
38762                        };
38763                        DataType::Custom {
38764                            name: type_name.to_string(),
38765                        }
38766                    } else {
38767                        let n = self.expect_number()? as u32;
38768                        self.expect(TokenType::RParen)?;
38769                        DataType::VarChar {
38770                            length: Some(n),
38771                            parenthesized_length: false,
38772                        }
38773                    }
38774                } else {
38775                    DataType::VarChar {
38776                        length: None,
38777                        parenthesized_length: false,
38778                    }
38779                }
38780            }
38781            // VARBINARY with optional (N) or (MAX) parameter
38782            "VARBINARY" => {
38783                if self.match_token(TokenType::LParen) {
38784                    if self.check(TokenType::RParen) {
38785                        self.skip();
38786                        DataType::VarBinary { length: None }
38787                    } else if self.check_identifier("MAX") {
38788                        self.skip();
38789                        self.expect(TokenType::RParen)?;
38790                        DataType::Custom {
38791                            name: "VARBINARY(MAX)".to_string(),
38792                        }
38793                    } else {
38794                        let n = self.expect_number()? as u32;
38795                        self.expect(TokenType::RParen)?;
38796                        DataType::VarBinary { length: Some(n) }
38797                    }
38798                } else {
38799                    DataType::VarBinary { length: None }
38800                }
38801            }
38802            // DECIMAL/NUMERIC with optional (precision, scale)
38803            "DECIMAL" | "NUMERIC" | "NUMBER" => {
38804                if self.match_token(TokenType::LParen) {
38805                    let precision = Some(self.expect_number()? as u32);
38806                    let scale = if self.match_token(TokenType::Comma) {
38807                        Some(self.expect_number()? as u32)
38808                    } else {
38809                        None
38810                    };
38811                    self.expect(TokenType::RParen)?;
38812                    DataType::Decimal { precision, scale }
38813                } else {
38814                    DataType::Decimal {
38815                        precision: None,
38816                        scale: None,
38817                    }
38818                }
38819            }
38820            // INT/INTEGER/BIGINT/SMALLINT/TINYINT with optional (N) display width
38821            "INT" | "INTEGER" => {
38822                let length = if self.match_token(TokenType::LParen) {
38823                    let n = Some(self.expect_number()? as u32);
38824                    self.expect(TokenType::RParen)?;
38825                    n
38826                } else {
38827                    None
38828                };
38829                DataType::Int {
38830                    length,
38831                    integer_spelling: name == "INTEGER",
38832                }
38833            }
38834            "BIGINT" => {
38835                let length = if self.match_token(TokenType::LParen) {
38836                    let n = Some(self.expect_number()? as u32);
38837                    self.expect(TokenType::RParen)?;
38838                    n
38839                } else {
38840                    None
38841                };
38842                DataType::BigInt { length }
38843            }
38844            "SMALLINT" => {
38845                let length = if self.match_token(TokenType::LParen) {
38846                    let n = Some(self.expect_number()? as u32);
38847                    self.expect(TokenType::RParen)?;
38848                    n
38849                } else {
38850                    None
38851                };
38852                DataType::SmallInt { length }
38853            }
38854            "TINYINT" => {
38855                let length = if self.match_token(TokenType::LParen) {
38856                    let n = Some(self.expect_number()? as u32);
38857                    self.expect(TokenType::RParen)?;
38858                    n
38859                } else {
38860                    None
38861                };
38862                DataType::TinyInt { length }
38863            }
38864            // FLOAT with optional (precision)
38865            "FLOAT" | "REAL" | "BINARY_FLOAT" => {
38866                let (precision, scale) = if self.match_token(TokenType::LParen) {
38867                    let n = Some(self.expect_number()? as u32);
38868                    let s = if self.match_token(TokenType::Comma) {
38869                        Some(self.expect_number()? as u32)
38870                    } else {
38871                        None
38872                    };
38873                    self.expect(TokenType::RParen)?;
38874                    (n, s)
38875                } else {
38876                    (None, None)
38877                };
38878                DataType::Float {
38879                    precision,
38880                    scale,
38881                    real_spelling: name == "REAL",
38882                }
38883            }
38884            "BINARY_DOUBLE" => DataType::Double {
38885                precision: None,
38886                scale: None,
38887            },
38888            // BINARY with optional (length)
38889            "BINARY" => {
38890                let length = if self.match_token(TokenType::LParen) {
38891                    let n = Some(self.expect_number()? as u32);
38892                    self.expect(TokenType::RParen)?;
38893                    n
38894                } else {
38895                    None
38896                };
38897                DataType::Binary { length }
38898            }
38899            // MySQL SIGNED [INTEGER] / UNSIGNED [INTEGER] in CAST context
38900            // CAST(x AS SIGNED INTEGER) -> CAST(x AS SIGNED)
38901            // CAST(x AS UNSIGNED INTEGER) -> CAST(x AS UNSIGNED)
38902            "SIGNED" | "UNSIGNED" => {
38903                // Consume optional INTEGER keyword after SIGNED/UNSIGNED
38904                if self.check_identifier("INTEGER")
38905                    || self.check_keyword_text("INTEGER")
38906                    || self.check_keyword_text("INT")
38907                {
38908                    self.skip();
38909                }
38910                DataType::Custom { name }
38911            }
38912            // ClickHouse Nullable(T) wrapper type
38913            "NULLABLE" => {
38914                self.expect(TokenType::LParen)?;
38915                let inner = self.parse_data_type_for_cast()?;
38916                self.expect(TokenType::RParen)?;
38917                DataType::Nullable {
38918                    inner: Box::new(inner),
38919                }
38920            }
38921            // For simple types, use convert_name_to_type to get proper DataType variants
38922            // This ensures VARCHAR becomes DataType::VarChar, not DataType::Custom
38923            // For user-defined types in generic mode, preserve original case from raw_name
38924            _ => {
38925                let base = self.convert_name_to_type(&name)?;
38926                // ClickHouse: consume parenthesized args for custom types like DateTime('UTC'),
38927                // LowCardinality(String), Variant(String, UInt64), JSON(max_dynamic_paths=8)
38928                if matches!(
38929                    self.config.dialect,
38930                    Some(crate::dialects::DialectType::ClickHouse)
38931                ) && self.check(TokenType::LParen)
38932                    && (matches!(
38933                        base,
38934                        DataType::Custom { .. } | DataType::Json | DataType::JsonB
38935                    ))
38936                {
38937                    self.skip(); // consume (
38938                    let args = self.parse_custom_type_args_balanced()?;
38939                    self.expect(TokenType::RParen)?;
38940                    let base_name = match &base {
38941                        DataType::Json => "JSON".to_string(),
38942                        DataType::JsonB => "JSONB".to_string(),
38943                        DataType::Custom { name } => name.clone(),
38944                        _ => unreachable!(),
38945                    };
38946                    DataType::Custom {
38947                        name: format!("{}({})", base_name, args),
38948                    }
38949                } else if matches!(base, DataType::Custom { .. }) && self.check(TokenType::Dot) {
38950                    // Handle schema-qualified user-defined types (e.g., app.status_enum)
38951                    // by consuming dot-separated identifiers like Python sqlglot's
38952                    // _parse_user_defined_type()
38953                    // Use raw_name to preserve original case for schema-qualified types
38954                    let mut type_name = raw_name.to_string();
38955                    while self.match_token(TokenType::Dot) {
38956                        let tok = self.advance();
38957                        type_name = format!("{}.{}", type_name, tok.text);
38958                    }
38959                    DataType::Custom { name: type_name }
38960                } else if matches!(base, DataType::Custom { .. }) && self.config.dialect.is_none() {
38961                    // Preserve original case for user-defined types in generic mode
38962                    DataType::Custom {
38963                        name: raw_name.to_string(),
38964                    }
38965                } else {
38966                    base
38967                }
38968            }
38969        };
38970
38971        // Materialize: handle postfix LIST syntax (INT LIST, INT LIST LIST LIST)
38972        let is_materialize = matches!(
38973            self.config.dialect,
38974            Some(crate::dialects::DialectType::Materialize)
38975        );
38976        let mut result_type = base_type;
38977        if is_materialize {
38978            while self.check_identifier("LIST") || self.check(TokenType::List) {
38979                self.skip(); // consume LIST
38980                result_type = DataType::List {
38981                    element_type: Box::new(result_type),
38982                };
38983            }
38984        }
38985
38986        // For dialects that support array type suffixes (DuckDB, PostgreSQL, Redshift),
38987        // parse array dimensions. For other dialects, brackets after a cast are subscript operations.
38988        if supports_array_type_suffix {
38989            self.maybe_parse_array_dimensions(result_type)
38990        } else {
38991            Ok(result_type)
38992        }
38993    }
38994
38995    /// Parse custom type arguments with balanced parentheses, preserving nested types
38996    fn parse_custom_type_args_balanced(&mut self) -> Result<String> {
38997        let mut depth = 0usize;
38998        let mut out = String::new();
38999        let mut prev_wordish = false;
39000
39001        while !self.is_at_end() {
39002            if self.check(TokenType::RParen) && depth == 0 {
39003                break;
39004            }
39005
39006            let token = self.advance();
39007            match token.token_type {
39008                TokenType::LParen => {
39009                    out.push('(');
39010                    depth += 1;
39011                    prev_wordish = false;
39012                }
39013                TokenType::RParen => {
39014                    if depth == 0 {
39015                        break;
39016                    }
39017                    depth -= 1;
39018                    out.push(')');
39019                    prev_wordish = true;
39020                }
39021                TokenType::Comma => {
39022                    out.push_str(", ");
39023                    prev_wordish = false;
39024                }
39025                TokenType::Eq => {
39026                    out.push_str(" = ");
39027                    prev_wordish = false;
39028                }
39029                TokenType::Plus => {
39030                    out.push_str(" + ");
39031                    prev_wordish = false;
39032                }
39033                TokenType::Dash => {
39034                    out.push('-');
39035                    prev_wordish = false;
39036                }
39037                TokenType::Dot => {
39038                    out.push('.');
39039                    prev_wordish = false;
39040                }
39041                TokenType::String | TokenType::DollarString => {
39042                    if prev_wordish {
39043                        out.push(' ');
39044                    }
39045                    let escaped = token.text.replace('\'', "''");
39046                    out.push('\'');
39047                    out.push_str(&escaped);
39048                    out.push('\'');
39049                    prev_wordish = true;
39050                }
39051                TokenType::Number | TokenType::Parameter => {
39052                    if prev_wordish {
39053                        out.push(' ');
39054                    }
39055                    out.push_str(&token.text);
39056                    prev_wordish = true;
39057                }
39058                TokenType::QuotedIdentifier => {
39059                    if prev_wordish {
39060                        out.push(' ');
39061                    }
39062                    out.push('"');
39063                    out.push_str(&token.text);
39064                    out.push('"');
39065                    prev_wordish = true;
39066                }
39067                _ => {
39068                    if prev_wordish {
39069                        out.push(' ');
39070                    }
39071                    out.push_str(&token.text);
39072                    prev_wordish = true;
39073                }
39074            }
39075        }
39076
39077        Ok(out)
39078    }
39079
39080    /// Uppercase the `skip` keyword in ClickHouse JSON type declarations.
39081    /// In ClickHouse, `SKIP col` within JSON(...) type specs must use uppercase SKIP.
39082    fn uppercase_json_type_skip_keyword(args: &str) -> String {
39083        // Replace "skip " at the start of the string or after ", " with "SKIP "
39084        let mut result = String::with_capacity(args.len());
39085        let mut rest = args;
39086        let mut at_start = true;
39087        while !rest.is_empty() {
39088            if at_start
39089                && rest.len() >= 5
39090                && rest[..4].eq_ignore_ascii_case("skip")
39091                && rest.as_bytes()[4] == b' '
39092            {
39093                result.push_str("SKIP");
39094                rest = &rest[4..];
39095                at_start = false;
39096            } else if rest.starts_with(", ") {
39097                result.push_str(", ");
39098                rest = &rest[2..];
39099                at_start = true;
39100            } else {
39101                result.push(rest.as_bytes()[0] as char);
39102                rest = &rest[1..];
39103                at_start = false;
39104            }
39105        }
39106        result
39107    }
39108
39109    /// Parse a data type from a text string by tokenizing and sub-parsing it.
39110    /// Used for ClickHouse JSON path types where a quoted identifier like "Array(JSON)"
39111    /// needs to be parsed as a proper structured DataType.
39112    fn parse_data_type_from_text(&mut self, text: &str) -> Result<DataType> {
39113        use crate::tokens::Tokenizer;
39114        let tokenizer = Tokenizer::default();
39115        let tokens = tokenizer.tokenize(text)?;
39116        if tokens.is_empty() {
39117            return Ok(DataType::Custom {
39118                name: text.to_string(),
39119            });
39120        }
39121        // Save parser state and temporarily swap in the sub-tokens
39122        let saved_tokens = std::mem::replace(&mut self.tokens, tokens);
39123        let saved_current = std::mem::replace(&mut self.current, 0);
39124        let result = self.parse_data_type();
39125        // Restore original parser state
39126        self.tokens = saved_tokens;
39127        self.current = saved_current;
39128        result
39129    }
39130
39131    /// Try to parse a data type optionally - returns None if no valid type found
39132    /// Used for JSON_TABLE column definitions where type may or may not be present
39133    fn parse_data_type_optional(&mut self) -> Result<Option<DataType>> {
39134        // Check if current token looks like a type name
39135        if !self.check(TokenType::Identifier)
39136            && !self.check(TokenType::Var)
39137            && !self.check_keyword()
39138        {
39139            return Ok(None);
39140        }
39141
39142        // Don't try to parse PATH as a type
39143        if self.check_identifier("PATH") {
39144            return Ok(None);
39145        }
39146
39147        // ClickHouse: ALIAS, EPHEMERAL, MATERIALIZED are column modifiers, not types
39148        if matches!(
39149            self.config.dialect,
39150            Some(crate::dialects::DialectType::ClickHouse)
39151        ) && (self.check_identifier("ALIAS")
39152            || self.check_identifier("EPHEMERAL")
39153            || self.check(TokenType::Materialized))
39154        {
39155            return Ok(None);
39156        }
39157
39158        let saved_pos = self.current;
39159        match self.parse_data_type() {
39160            Ok(dt) => Ok(Some(dt)),
39161            Err(_) => {
39162                self.current = saved_pos;
39163                Ok(None)
39164            }
39165        }
39166    }
39167
39168    /// Convert a DataType to a string representation for JSONColumnDef.kind
39169    fn data_type_to_string(&self, dt: &DataType) -> String {
39170        match dt {
39171            DataType::Int {
39172                length: Some(n),
39173                integer_spelling: true,
39174            } => format!("INTEGER({})", n),
39175            DataType::Int {
39176                length: Some(n), ..
39177            } => format!("INT({})", n),
39178            DataType::Int {
39179                length: None,
39180                integer_spelling: true,
39181            } => "INTEGER".to_string(),
39182            DataType::Int { length: None, .. } => "INT".to_string(),
39183            DataType::BigInt { length: Some(n) } => format!("BIGINT({})", n),
39184            DataType::BigInt { length: None } => "BIGINT".to_string(),
39185            DataType::SmallInt { length: Some(n) } => format!("SMALLINT({})", n),
39186            DataType::SmallInt { length: None } => "SMALLINT".to_string(),
39187            DataType::TinyInt { length: Some(n) } => format!("TINYINT({})", n),
39188            DataType::TinyInt { length: None } => "TINYINT".to_string(),
39189            DataType::Float {
39190                precision: Some(p),
39191                scale: Some(s),
39192                ..
39193            } => format!("FLOAT({}, {})", p, s),
39194            DataType::Float {
39195                precision: Some(p),
39196                scale: None,
39197                ..
39198            } => format!("FLOAT({})", p),
39199            DataType::Float {
39200                precision: None, ..
39201            } => "FLOAT".to_string(),
39202            DataType::Double {
39203                precision: Some(p),
39204                scale: Some(s),
39205            } => format!("DOUBLE({}, {})", p, s),
39206            DataType::Double {
39207                precision: Some(p),
39208                scale: None,
39209            } => format!("DOUBLE({})", p),
39210            DataType::Double {
39211                precision: None, ..
39212            } => "DOUBLE".to_string(),
39213            DataType::Decimal {
39214                precision: Some(p),
39215                scale: Some(s),
39216            } => format!("DECIMAL({}, {})", p, s),
39217            DataType::Decimal {
39218                precision: Some(p),
39219                scale: None,
39220            } => format!("DECIMAL({})", p),
39221            DataType::Decimal {
39222                precision: None, ..
39223            } => "DECIMAL".to_string(),
39224            DataType::VarChar {
39225                length: Some(n), ..
39226            } => format!("VARCHAR({})", n),
39227            DataType::VarChar { length: None, .. } => "VARCHAR".to_string(),
39228            DataType::Char { length: Some(n) } => format!("CHAR({})", n),
39229            DataType::Char { length: None } => "CHAR".to_string(),
39230            DataType::Text => "TEXT".to_string(),
39231            DataType::Boolean => "BOOLEAN".to_string(),
39232            DataType::Date => "DATE".to_string(),
39233            DataType::Time {
39234                precision: Some(p), ..
39235            } => format!("TIME({})", p),
39236            DataType::Time {
39237                precision: None, ..
39238            } => "TIME".to_string(),
39239            DataType::Timestamp {
39240                precision: Some(p),
39241                timezone: true,
39242            } => format!("TIMESTAMPTZ({})", p),
39243            DataType::Timestamp {
39244                precision: Some(p),
39245                timezone: false,
39246            } => format!("TIMESTAMP({})", p),
39247            DataType::Timestamp {
39248                precision: None,
39249                timezone: true,
39250            } => "TIMESTAMPTZ".to_string(),
39251            DataType::Timestamp {
39252                precision: None,
39253                timezone: false,
39254            } => "TIMESTAMP".to_string(),
39255            DataType::Json => "JSON".to_string(),
39256            DataType::JsonB => "JSONB".to_string(),
39257            DataType::Binary { length: Some(n) } => format!("BINARY({})", n),
39258            DataType::Binary { length: None } => "BINARY".to_string(),
39259            DataType::VarBinary { length: Some(n) } => format!("VARBINARY({})", n),
39260            DataType::VarBinary { length: None } => "VARBINARY".to_string(),
39261            DataType::String { length: Some(n) } => format!("STRING({})", n),
39262            DataType::String { length: None } => "STRING".to_string(),
39263            DataType::Array { element_type, .. } => {
39264                format!("ARRAY({})", self.data_type_to_string(element_type))
39265            }
39266            DataType::Nullable { inner } => {
39267                format!("Nullable({})", self.data_type_to_string(inner))
39268            }
39269            DataType::Custom { name } => name.clone(),
39270            _ => format!("{:?}", dt),
39271        }
39272    }
39273
39274    /// Parse optional array dimensions after a type: [], [N], [N][M], ARRAY, ARRAY[N], etc.
39275    fn maybe_parse_array_dimensions(&mut self, base_type: DataType) -> Result<DataType> {
39276        let mut current_type = base_type;
39277
39278        // Handle PostgreSQL ARRAY keyword suffix: type ARRAY or type ARRAY[3]
39279        if self.check_identifier("ARRAY") {
39280            self.skip(); // consume ARRAY
39281                         // Check for optional dimension: ARRAY[N]
39282            let dimension = if self.match_token(TokenType::LBracket) {
39283                let dim = if self.check(TokenType::Number) {
39284                    let n = self.expect_number()? as u32;
39285                    Some(n)
39286                } else {
39287                    None
39288                };
39289                self.expect(TokenType::RBracket)?;
39290                dim
39291            } else {
39292                None
39293            };
39294            current_type = DataType::Array {
39295                element_type: Box::new(current_type),
39296                dimension,
39297            };
39298        }
39299
39300        // Handle bracket-based array dimensions: TYPE[], TYPE[N], TYPE[][N], etc.
39301        while self.match_token(TokenType::LBracket) {
39302            // Check for optional dimension: [N] or just []
39303            let dimension = if self.check(TokenType::Number) {
39304                let n = self.expect_number()? as u32;
39305                Some(n)
39306            } else {
39307                None
39308            };
39309            self.expect(TokenType::RBracket)?;
39310
39311            current_type = DataType::Array {
39312                element_type: Box::new(current_type),
39313                dimension,
39314            };
39315        }
39316
39317        Ok(current_type)
39318    }
39319
39320    /// Parse spatial type arguments like GEOMETRY(Point, 4326) or GEOGRAPHY
39321    fn parse_spatial_type_args(&mut self) -> Result<(Option<String>, Option<u32>)> {
39322        if self.match_token(TokenType::LParen) {
39323            // First arg can be a subtype name (POINT, LINESTRING, etc.) or a numeric dimension
39324            if self.check(TokenType::Number) {
39325                // Numeric argument (e.g., ST_GEOMETRY(1) in Teradata)
39326                let n = self.expect_number()? as u32;
39327                self.expect(TokenType::RParen)?;
39328                return Ok((None, Some(n)));
39329            }
39330            // Parse subtype
39331            let subtype = Some(self.expect_identifier()?.to_ascii_uppercase());
39332
39333            // Parse optional SRID
39334            let srid = if self.match_token(TokenType::Comma) {
39335                Some(self.expect_number()? as u32)
39336            } else {
39337                None
39338            };
39339
39340            self.expect(TokenType::RParen)?;
39341            Ok((subtype, srid))
39342        } else {
39343            Ok((None, None))
39344        }
39345    }
39346
39347    /// Parse struct/row/union type fields: name TYPE, name TYPE, ...
39348    /// `paren_style` indicates whether we're parsing parenthesized syntax (terminates at RParen)
39349    /// or angle-bracket syntax (terminates at Gt/GtGt).
39350    fn parse_struct_type_fields(&mut self, paren_style: bool) -> Result<Vec<StructField>> {
39351        let mut fields = Vec::new();
39352        // Check for empty field list
39353        if (paren_style && self.check(TokenType::RParen))
39354            || (!paren_style && (self.check(TokenType::Gt) || self.check(TokenType::GtGt)))
39355        {
39356            return Ok(fields);
39357        }
39358        loop {
39359            // Parse field name or just type (for anonymous struct fields)
39360            // Track whether it was a quoted identifier to preserve quoting
39361            let is_quoted = self.check(TokenType::QuotedIdentifier);
39362            let first = self.expect_identifier_or_keyword()?;
39363            let first_upper = first.to_ascii_uppercase();
39364
39365            // Check if this is a parametric type (ARRAY<T>, MAP<K,V>, STRUCT<...>, STRUCT(...))
39366            let is_parametric_type = (first_upper == "ARRAY"
39367                || first_upper == "MAP"
39368                || first_upper == "STRUCT"
39369                || first_upper == "ROW")
39370                && (self.check(TokenType::Lt) || self.check(TokenType::LParen));
39371
39372            let (field_name, field_type) = if is_parametric_type {
39373                // This is a parametric type as an anonymous field
39374                let field_type = self.parse_data_type_from_name(&first_upper)?;
39375                (String::new(), field_type)
39376            } else if self.check(TokenType::Comma)
39377                || self.match_identifier("OPTIONS")  // Check for OPTIONS (but don't consume yet)
39378                || (paren_style && self.check(TokenType::RParen))
39379                || (!paren_style && (self.check(TokenType::Gt) || self.check(TokenType::GtGt)))
39380            {
39381                // Check if we just matched OPTIONS - if so, retreat
39382                if self.previous().text.eq_ignore_ascii_case("OPTIONS") {
39383                    self.current -= 1;
39384                }
39385                // Anonymous field: just a type name
39386                let field_type = self.convert_name_to_type(&first)?;
39387                (String::new(), field_type)
39388            } else if self.is_identifier_token()
39389                || self.is_safe_keyword_as_identifier()
39390                || self.check(TokenType::Lt)
39391                || self.check(TokenType::LParen)
39392                || self.check(TokenType::Colon)
39393            {
39394                // Named field: fieldname TYPE (or fieldname: TYPE for Hive)
39395                // Consume optional colon separator (Hive-style: `STRUCT<field_name: TYPE>`)
39396                self.match_token(TokenType::Colon);
39397                let field_type = self.parse_data_type()?;
39398                // Preserve quoting for field names
39399                let field_name = if is_quoted {
39400                    format!("\"{}\"", first)
39401                } else {
39402                    first
39403                };
39404                (field_name, field_type)
39405            } else {
39406                // Just a type name
39407                let field_type = self.convert_name_to_type(&first)?;
39408                (String::new(), field_type)
39409            };
39410
39411            // Spark/Databricks: Check for COMMENT clause on struct field
39412            let comment = if self.match_token(TokenType::Comment) {
39413                Some(self.expect_string()?)
39414            } else {
39415                None
39416            };
39417
39418            // BigQuery: Check for OPTIONS clause on struct field
39419            let options = if self.match_identifier("OPTIONS") {
39420                self.parse_options_list()?
39421            } else {
39422                Vec::new()
39423            };
39424
39425            fields.push(StructField::with_options_and_comment(
39426                field_name, field_type, options, comment,
39427            ));
39428
39429            if !self.match_token(TokenType::Comma) {
39430                break;
39431            }
39432        }
39433        Ok(fields)
39434    }
39435
39436    /// Parse a data type given a name that was already consumed
39437    /// This is used for standalone type expressions like ARRAY<T>
39438    fn parse_data_type_from_name(&mut self, name: &str) -> Result<DataType> {
39439        match name {
39440            "ARRAY" => {
39441                if self.match_token(TokenType::Lt) {
39442                    let element_type = self.parse_data_type()?;
39443                    self.expect_gt()?;
39444                    Ok(DataType::Array {
39445                        element_type: Box::new(element_type),
39446                        dimension: None,
39447                    })
39448                } else {
39449                    Ok(DataType::Custom {
39450                        name: "ARRAY".to_string(),
39451                    })
39452                }
39453            }
39454            "MAP" => {
39455                if self.match_token(TokenType::Lt) {
39456                    let key_type = self.parse_data_type()?;
39457                    self.expect(TokenType::Comma)?;
39458                    let value_type = self.parse_data_type()?;
39459                    self.expect_gt()?;
39460                    Ok(DataType::Map {
39461                        key_type: Box::new(key_type),
39462                        value_type: Box::new(value_type),
39463                    })
39464                } else {
39465                    Ok(DataType::Custom {
39466                        name: "MAP".to_string(),
39467                    })
39468                }
39469            }
39470            "STRUCT" => {
39471                if self.match_token(TokenType::Lt) {
39472                    let fields = self.parse_struct_type_fields(false)?;
39473                    self.expect_gt()?;
39474                    Ok(DataType::Struct {
39475                        fields,
39476                        nested: false,
39477                    })
39478                } else if self.match_token(TokenType::LParen) {
39479                    let fields = self.parse_struct_type_fields(true)?;
39480                    self.expect(TokenType::RParen)?;
39481                    Ok(DataType::Struct {
39482                        fields,
39483                        nested: true,
39484                    })
39485                } else {
39486                    Ok(DataType::Custom {
39487                        name: "STRUCT".to_string(),
39488                    })
39489                }
39490            }
39491            "ROW" => {
39492                if self.match_token(TokenType::LParen) {
39493                    let fields = self.parse_struct_type_fields(true)?;
39494                    self.expect(TokenType::RParen)?;
39495                    Ok(DataType::Struct {
39496                        fields,
39497                        nested: true,
39498                    })
39499                } else {
39500                    Ok(DataType::Custom {
39501                        name: "ROW".to_string(),
39502                    })
39503                }
39504            }
39505            _ => Ok(DataType::Custom {
39506                name: name.to_string(),
39507            }),
39508        }
39509    }
39510
39511    /// Convert a type name string to a DataType
39512    /// Used for anonymous struct fields where we have just a type name
39513    fn convert_name_to_type(&self, name: &str) -> Result<DataType> {
39514        let upper = name.to_ascii_uppercase();
39515        Ok(match upper.as_str() {
39516            "INT" => DataType::Int {
39517                length: None,
39518                integer_spelling: false,
39519            },
39520            "INTEGER" => DataType::Int {
39521                length: None,
39522                integer_spelling: true,
39523            },
39524            "BIGINT" => DataType::BigInt { length: None },
39525            "SMALLINT" => DataType::SmallInt { length: None },
39526            "TINYINT" => DataType::TinyInt { length: None },
39527            "FLOAT" | "BINARY_FLOAT" => DataType::Float {
39528                precision: None,
39529                scale: None,
39530                real_spelling: false,
39531            },
39532            "REAL" => DataType::Float {
39533                precision: None,
39534                scale: None,
39535                real_spelling: true,
39536            },
39537            "DOUBLE" | "BINARY_DOUBLE" => DataType::Double {
39538                precision: None,
39539                scale: None,
39540            },
39541            "DECIMAL" | "NUMERIC" => DataType::Decimal {
39542                precision: None,
39543                scale: None,
39544            },
39545            "BOOLEAN" | "BOOL" => DataType::Boolean,
39546            "CHAR" | "CHARACTER" | "NCHAR" => DataType::Char { length: None },
39547            "VARCHAR" | "NVARCHAR" => DataType::VarChar {
39548                length: None,
39549                parenthesized_length: false,
39550            },
39551            "TEXT" | "STRING" | "NTEXT" => DataType::Text,
39552            "DATE" => DataType::Date,
39553            "TIME" => DataType::Time {
39554                precision: None,
39555                timezone: false,
39556            },
39557            "TIMETZ" => DataType::Time {
39558                precision: None,
39559                timezone: true,
39560            },
39561            "TIMESTAMP" => DataType::Timestamp {
39562                precision: None,
39563                timezone: false,
39564            },
39565            "INTERVAL" => DataType::Interval {
39566                unit: None,
39567                to: None,
39568            },
39569            "JSON" => DataType::Json,
39570            "JSONB" => DataType::JsonB,
39571            "UUID" => DataType::Uuid,
39572            "BLOB" => DataType::Blob,
39573            "BYTEA" => DataType::VarBinary { length: None },
39574            "BINARY" => DataType::Binary { length: None },
39575            "VARBINARY" => DataType::VarBinary { length: None },
39576            "BIT" => DataType::Bit { length: None },
39577            "VARBIT" => DataType::VarBit { length: None },
39578            _ => DataType::Custom {
39579                name: name.to_string(),
39580            },
39581        })
39582    }
39583
39584    /// Parse star modifiers: EXCLUDE/EXCEPT, REPLACE, RENAME
39585    /// Syntax varies by dialect:
39586    /// - DuckDB: * EXCLUDE (col1, col2)
39587    /// - BigQuery: * EXCEPT (col1, col2), * REPLACE (expr AS col)
39588    /// - Snowflake: * EXCLUDE col, * RENAME (old AS new)
39589    fn parse_star_modifiers(&mut self, table: Option<Identifier>) -> Result<Star> {
39590        self.parse_star_modifiers_with_comments(table, Vec::new())
39591    }
39592
39593    /// Parse star modifiers with explicit trailing comments from the star token
39594    fn parse_star_modifiers_with_comments(
39595        &mut self,
39596        table: Option<Identifier>,
39597        star_trailing_comments: Vec<String>,
39598    ) -> Result<Star> {
39599        let mut except = None;
39600        let mut replace = None;
39601        let mut rename = None;
39602
39603        // Parse EXCLUDE / EXCEPT clause
39604        if self.match_token(TokenType::Exclude) || self.match_token(TokenType::Except) {
39605            // ClickHouse: EXCEPT STRICT col1, col2 (STRICT is optional modifier)
39606            let _ = self.match_text_seq(&["STRICT"]);
39607            let mut columns = Vec::new();
39608            if self.match_token(TokenType::LParen) {
39609                // EXCLUDE (col1, col2) or EXCEPT (A.COL_1, B.COL_2)
39610                loop {
39611                    // ClickHouse: allow string literals in EXCEPT ('col_regex')
39612                    // and keywords like 'key', 'index' as column names
39613                    let col = if self.check(TokenType::String) {
39614                        self.advance().text
39615                    } else if self.is_safe_keyword_as_identifier() {
39616                        self.advance().text
39617                    } else {
39618                        self.expect_identifier()?
39619                    };
39620                    // Handle qualified column names like A.COL_1
39621                    if self.match_token(TokenType::Dot) {
39622                        let subcol = if self.is_safe_keyword_as_identifier() {
39623                            self.advance().text
39624                        } else {
39625                            self.expect_identifier()?
39626                        };
39627                        columns.push(Identifier::new(format!("{}.{}", col, subcol)));
39628                    } else {
39629                        columns.push(Identifier::new(col));
39630                    }
39631                    if !self.match_token(TokenType::Comma) {
39632                        break;
39633                    }
39634                }
39635                self.expect(TokenType::RParen)?;
39636            } else {
39637                // EXCLUDE col (single column, Snowflake) or EXCEPT col1, col2 (ClickHouse)
39638                // or EXCEPT 'regex' (ClickHouse)
39639                loop {
39640                    let col = if self.check(TokenType::String) {
39641                        self.advance().text
39642                    } else if self.is_safe_keyword_as_identifier() {
39643                        self.advance().text
39644                    } else {
39645                        self.expect_identifier()?
39646                    };
39647                    columns.push(Identifier::new(col));
39648                    // ClickHouse allows comma-separated columns without parens: EXCEPT col1, col2
39649                    // But only if the next token after comma looks like a column name
39650                    if !matches!(
39651                        self.config.dialect,
39652                        Some(crate::dialects::DialectType::ClickHouse)
39653                    ) || !self.check(TokenType::Comma)
39654                        || !matches!(
39655                            self.peek_nth(1).map(|t| t.token_type),
39656                            Some(TokenType::Identifier)
39657                                | Some(TokenType::QuotedIdentifier)
39658                                | Some(TokenType::Var)
39659                                | Some(TokenType::String)
39660                        )
39661                    {
39662                        break;
39663                    }
39664                    self.skip(); // consume comma
39665                }
39666            }
39667            except = Some(columns);
39668        }
39669
39670        // Parse REPLACE clause
39671        if self.match_token(TokenType::Replace) {
39672            // ClickHouse: REPLACE STRICT is optional modifier
39673            let _ = self.match_text_seq(&["STRICT"]);
39674            let mut replacements = Vec::new();
39675            if self.match_token(TokenType::LParen) {
39676                loop {
39677                    let expr = self.parse_expression()?;
39678                    self.expect(TokenType::As)?;
39679                    let alias = self.expect_identifier_or_keyword()?;
39680                    replacements.push(Alias::new(expr, Identifier::new(alias)));
39681                    if !self.match_token(TokenType::Comma) {
39682                        break;
39683                    }
39684                }
39685                self.expect(TokenType::RParen)?;
39686            } else if matches!(
39687                self.config.dialect,
39688                Some(crate::dialects::DialectType::ClickHouse)
39689            ) {
39690                // ClickHouse: REPLACE [STRICT] expr AS name (single entry without parens)
39691                // Multiple entries require parens: REPLACE(expr1 AS name1, expr2 AS name2)
39692                let expr = self.parse_expression()?;
39693                self.expect(TokenType::As)?;
39694                let alias = self.expect_identifier_or_keyword()?;
39695                replacements.push(Alias::new(expr, Identifier::new(alias)));
39696            } else {
39697                return Err(self.parse_error("Expected LParen after REPLACE"));
39698            }
39699            replace = Some(replacements);
39700        }
39701
39702        // Parse RENAME clause (Snowflake)
39703        if self.match_token(TokenType::Rename) {
39704            let mut renames = Vec::new();
39705            if self.match_token(TokenType::LParen) {
39706                loop {
39707                    let old_name = self.expect_identifier()?;
39708                    self.expect(TokenType::As)?;
39709                    let new_name = self.expect_identifier()?;
39710                    renames.push((Identifier::new(old_name), Identifier::new(new_name)));
39711                    if !self.match_token(TokenType::Comma) {
39712                        break;
39713                    }
39714                }
39715                self.expect(TokenType::RParen)?;
39716            } else {
39717                // Single rename without parens
39718                let old_name = self.expect_identifier()?;
39719                self.expect(TokenType::As)?;
39720                let new_name = self.expect_identifier()?;
39721                renames.push((Identifier::new(old_name), Identifier::new(new_name)));
39722            }
39723            rename = Some(renames);
39724        }
39725
39726        Ok(Star {
39727            table,
39728            except,
39729            replace,
39730            rename,
39731            trailing_comments: star_trailing_comments,
39732            span: None,
39733        })
39734    }
39735
39736    // === Helper methods ===
39737
39738    /// Check if at end of tokens
39739    #[inline]
39740    fn is_at_end(&self) -> bool {
39741        self.current >= self.tokens.len()
39742    }
39743
39744    /// Check if current token is a query modifier keyword or end of input.
39745    /// Used after GROUP BY ALL/DISTINCT to decide whether to parse expression lists.
39746    fn is_at_query_modifier_or_end(&self) -> bool {
39747        if self.is_at_end() {
39748            return true;
39749        }
39750        matches!(
39751            self.peek().token_type,
39752            TokenType::Having
39753                | TokenType::Qualify
39754                | TokenType::Window
39755                | TokenType::Order
39756                | TokenType::Limit
39757                | TokenType::Fetch
39758                | TokenType::Offset
39759                | TokenType::For
39760                | TokenType::Lock
39761                | TokenType::Union
39762                | TokenType::Except
39763                | TokenType::Intersect
39764                | TokenType::RParen
39765                | TokenType::Semicolon
39766                | TokenType::Where
39767        )
39768    }
39769
39770    /// Create a parse error with position from the current token
39771    fn parse_error(&self, message: impl Into<String>) -> Error {
39772        let span = self.peek().span;
39773        Error::parse(message, span.line, span.column, span.start, span.end)
39774    }
39775
39776    /// Peek at current token
39777    /// Returns reference to current token, or last token if at end
39778    #[inline]
39779    fn peek(&self) -> &Token {
39780        if self.current >= self.tokens.len() {
39781            // Return last token as fallback when at end
39782            // In practice, callers should check is_at_end() before calling peek()
39783            // but this prevents panic
39784            self.tokens.last().expect("Token list should not be empty")
39785        } else {
39786            &self.tokens[self.current]
39787        }
39788    }
39789
39790    /// Look ahead by n positions (0 = current token)
39791    fn peek_nth(&self, n: usize) -> Option<&Token> {
39792        let idx = self.current + n;
39793        if idx < self.tokens.len() {
39794            Some(&self.tokens[idx])
39795        } else {
39796            None
39797        }
39798    }
39799
39800    /// Advance to next token
39801    #[inline]
39802    fn advance(&mut self) -> Token {
39803        if self.current >= self.tokens.len() {
39804            // Return last token as fallback if we're past the end
39805            // In practice, callers should check is_at_end() before calling advance()
39806            return self
39807                .tokens
39808                .last()
39809                .cloned()
39810                .expect("Token list should not be empty");
39811        }
39812        let token = self.tokens[self.current].clone();
39813        self.current += 1;
39814        token
39815    }
39816
39817    /// Advance to next token without returning it (when result is unused)
39818    #[inline]
39819    fn skip(&mut self) {
39820        if self.current < self.tokens.len() {
39821            self.current += 1;
39822        }
39823    }
39824
39825    /// Get the previous token (last consumed)
39826    fn previous(&self) -> &Token {
39827        &self.tokens[self.current - 1]
39828    }
39829
39830    /// Get trailing comments from the previous token
39831    fn previous_trailing_comments(&self) -> &[String] {
39832        if self.current > 0 {
39833            &self.tokens[self.current - 1].trailing_comments
39834        } else {
39835            &[]
39836        }
39837    }
39838
39839    /// Get the token type of the previous token (the one before current).
39840    fn previous_token_type(&self) -> Option<TokenType> {
39841        if self.current > 0 {
39842            Some(self.tokens[self.current - 1].token_type.clone())
39843        } else {
39844            None
39845        }
39846    }
39847
39848    /// Wrap a query expression in a Subquery node.
39849    /// Only wraps if the expression is a query statement (Select, Union, etc.),
39850    /// not for simple expressions like column references.
39851    fn maybe_wrap_in_subquery(&self, inner: Expression) -> Expression {
39852        if matches!(
39853            &inner,
39854            Expression::Select(_)
39855                | Expression::Union(_)
39856                | Expression::Intersect(_)
39857                | Expression::Except(_)
39858        ) {
39859            Expression::Subquery(Box::new(Subquery {
39860                this: inner,
39861                alias: None,
39862                column_aliases: Vec::new(),
39863                order_by: None,
39864                limit: None,
39865                offset: None,
39866                distribute_by: None,
39867                sort_by: None,
39868                cluster_by: None,
39869                lateral: false,
39870                modifiers_inside: false,
39871                trailing_comments: Vec::new(),
39872                inferred_type: None,
39873            }))
39874        } else {
39875            inner
39876        }
39877    }
39878
39879    /// Clear trailing_comments from the rightmost leaf of an expression tree.
39880    /// Used by parse_and/parse_or to avoid comment duplication: when the same comment
39881    /// is captured both in an expression's trailing_comments (during parse_primary) and
39882    /// in a BinaryOp's operator_comments (during parse_and/parse_or), we clear the
39883    /// expression's copy since the operator_comments position (after AND/OR) is correct.
39884    fn clear_rightmost_trailing_comments(expr: &mut Expression) {
39885        match expr {
39886            Expression::Column(col) => col.trailing_comments.clear(),
39887            Expression::And(op) | Expression::Or(op) => {
39888                Self::clear_rightmost_trailing_comments(&mut op.right);
39889            }
39890            Expression::Not(op) => {
39891                Self::clear_rightmost_trailing_comments(&mut op.this);
39892            }
39893            // For comparison ops, the rightmost is the right operand
39894            Expression::Eq(op)
39895            | Expression::Neq(op)
39896            | Expression::Lt(op)
39897            | Expression::Lte(op)
39898            | Expression::Gt(op)
39899            | Expression::Gte(op)
39900            | Expression::Add(op)
39901            | Expression::Sub(op)
39902            | Expression::Mul(op)
39903            | Expression::Div(op) => {
39904                Self::clear_rightmost_trailing_comments(&mut op.right);
39905            }
39906            // For other expressions, trailing_comments might be stored differently
39907            // We don't need to handle all variants, just the common ones that appear
39908            // as operands in AND/OR expressions
39909            _ => {}
39910        }
39911    }
39912
39913    /// Get leading comments from the current token (comments that appeared before it)
39914    fn current_leading_comments(&self) -> &[String] {
39915        if !self.is_at_end() {
39916            &self.tokens[self.current].comments
39917        } else {
39918            &[]
39919        }
39920    }
39921
39922    /// Convert a slice of tokens to SQL string with proper quoting for strings
39923    fn tokens_to_sql(&self, start: usize, end: usize) -> String {
39924        let mut result = String::new();
39925        let mut prev_line: Option<usize> = None;
39926        let mut prev_end_offset: Option<usize> = None;
39927
39928        for t in &self.tokens[start..end] {
39929            // Check if we moved to a new line (preserve original line structure)
39930            let is_new_line = prev_line.is_some() && t.span.line > prev_line.unwrap();
39931
39932            // Use byte offsets to determine original spacing between tokens.
39933            // This preserves the exact spacing from the source (e.g., TRANSFORM( vs OPTIONS ())
39934            if is_new_line {
39935                result.push('\n');
39936                // Preserve original indentation
39937                // span.column is the column AFTER the last character (1-based),
39938                // so start column = span.column - text.chars().count()
39939                let text_len = t.text.chars().count();
39940                let start_col = t.span.column.saturating_sub(text_len);
39941                // For string tokens, add 2 for the quotes that were stripped
39942                let start_col = if t.token_type == TokenType::String {
39943                    start_col.saturating_sub(2)
39944                } else {
39945                    start_col
39946                };
39947                let indent = if start_col > 1 { start_col - 1 } else { 0 };
39948                for _ in 0..indent {
39949                    result.push(' ');
39950                }
39951            } else if !result.is_empty() {
39952                // Same line: use byte offsets to detect if there was whitespace
39953                let had_space = prev_end_offset.map_or(false, |prev_end| t.span.start > prev_end);
39954                if had_space {
39955                    result.push(' ');
39956                }
39957            }
39958
39959            if t.token_type == TokenType::String {
39960                // Re-add quotes around string literals
39961                result.push('\'');
39962                result.push_str(&t.text.replace('\'', "''"));
39963                result.push('\'');
39964            } else {
39965                result.push_str(&t.text);
39966            }
39967
39968            prev_line = Some(t.span.line);
39969            prev_end_offset = Some(t.span.end);
39970        }
39971        result
39972    }
39973
39974    /// Convert tokens to SQL for CREATE STAGE, normalizing FILE_FORMAT clause
39975    /// Transforms FILE_FORMAT='value' to FILE_FORMAT=(FORMAT_NAME='value')
39976    /// and FILE_FORMAT=schema.format to FILE_FORMAT=(FORMAT_NAME=schema.format)
39977    fn tokens_to_sql_stage_format(&self, start: usize, end: usize) -> String {
39978        let mut result = String::new();
39979        let mut prev_token_type: Option<TokenType> = None;
39980        let mut i = start;
39981
39982        while i < end {
39983            let t = &self.tokens[i];
39984
39985            // Check for FILE_FORMAT= pattern that needs normalization
39986            // FILE_FORMAT must be followed by = and then NOT by (
39987            if (t.token_type == TokenType::Var || t.token_type == TokenType::Identifier)
39988                && t.text.eq_ignore_ascii_case("FILE_FORMAT")
39989                && i + 1 < end
39990                && self.tokens[i + 1].token_type == TokenType::Eq
39991                && (i + 2 >= end || self.tokens[i + 2].token_type != TokenType::LParen)
39992            {
39993                // Need to normalize: FILE_FORMAT=value -> FILE_FORMAT=(FORMAT_NAME=value)
39994                if !result.is_empty() && prev_token_type != Some(TokenType::LParen) {
39995                    result.push(' ');
39996                }
39997                result.push_str("FILE_FORMAT=(FORMAT_NAME=");
39998
39999                // Skip FILE_FORMAT and =
40000                i += 2;
40001
40002                // Collect the value (string literal or qualified identifier like schema.format)
40003                while i < end {
40004                    let val = &self.tokens[i];
40005                    if val.token_type == TokenType::String {
40006                        // String literal: 'format1'
40007                        result.push('\'');
40008                        result.push_str(&val.text.replace('\'', "''"));
40009                        result.push('\'');
40010                        i += 1;
40011                        break;
40012                    } else if val.token_type == TokenType::Var
40013                        || val.token_type == TokenType::Identifier
40014                    {
40015                        // Identifier: schema1 or format1
40016                        result.push_str(&val.text);
40017                        i += 1;
40018                        // Check for dot (qualified name)
40019                        if i < end && self.tokens[i].token_type == TokenType::Dot {
40020                            result.push('.');
40021                            i += 1;
40022                            // Expect identifier after dot
40023                            if i < end {
40024                                result.push_str(&self.tokens[i].text);
40025                                i += 1;
40026                            }
40027                        }
40028                        break;
40029                    } else {
40030                        break;
40031                    }
40032                }
40033                result.push(')');
40034                prev_token_type = Some(TokenType::RParen);
40035                continue;
40036            }
40037
40038            // Normal token handling (same as tokens_to_sql)
40039            let needs_space = !result.is_empty()
40040                && prev_token_type != Some(TokenType::LParen)
40041                && prev_token_type != Some(TokenType::Eq)
40042                && prev_token_type != Some(TokenType::Dot)
40043                && t.token_type != TokenType::Comma
40044                && t.token_type != TokenType::RParen
40045                && t.token_type != TokenType::LParen
40046                && t.token_type != TokenType::Eq
40047                && t.token_type != TokenType::Dot;
40048
40049            if needs_space {
40050                result.push(' ');
40051            }
40052
40053            if t.token_type == TokenType::String {
40054                result.push('\'');
40055                result.push_str(&t.text.replace('\'', "''"));
40056                result.push('\'');
40057            } else {
40058                result.push_str(&t.text);
40059            }
40060
40061            prev_token_type = Some(t.token_type);
40062            i += 1;
40063        }
40064        result
40065    }
40066
40067    /// Like tokens_to_sql but also uppercases keyword tokens and adds space after commas
40068    fn tokens_to_sql_uppercased(&self, start: usize, end: usize) -> String {
40069        let mut result = String::new();
40070        let mut prev_token_type: Option<TokenType> = None;
40071        let mut prev_token_text: Option<String> = None;
40072
40073        for t in &self.tokens[start..end] {
40074            // Smart spacing: no space before comma, ), . or after (, .
40075            // Add space before ( only when preceded by a structural keyword or identifier
40076            // (e.g., "PRIMARY KEY (Id)", "CLUSTERED (EmpID)")
40077            // but NOT after data type keywords (e.g., "VARCHAR(100)", "INT(11)")
40078            let is_lparen_after_keyword = t.token_type == TokenType::LParen
40079                && prev_token_type.map_or(false, |p: TokenType| {
40080                    // Only add space for structural SQL keywords, not data type keywords
40081                    match p {
40082                        TokenType::PrimaryKey | TokenType::ForeignKey | TokenType::Unique
40083                        | TokenType::Check | TokenType::Index | TokenType::Key
40084                        | TokenType::Constraint | TokenType::References
40085                        | TokenType::Not | TokenType::Null
40086                        | TokenType::Default | TokenType::Values | TokenType::In
40087                        | TokenType::Exists | TokenType::Select | TokenType::From
40088                        | TokenType::Where | TokenType::Having | TokenType::Using
40089                        | TokenType::On | TokenType::Set | TokenType::Into
40090                        | TokenType::Table | TokenType::View | TokenType::Create
40091                        | TokenType::Insert | TokenType::Update | TokenType::Delete
40092                        | TokenType::Join | TokenType::Left | TokenType::Right
40093                        | TokenType::Inner | TokenType::Outer | TokenType::Full
40094                        | TokenType::Cross | TokenType::Case | TokenType::When
40095                        | TokenType::Then | TokenType::Else | TokenType::End
40096                        | TokenType::If | TokenType::Partition | TokenType::Over
40097                        | TokenType::Between | TokenType::Like | TokenType::Replace
40098                        | TokenType::Grant | TokenType::Revoke
40099                        => true,
40100                        _ => false,
40101                    }
40102                })
40103                // For Var/Identifier tokens, add space before ( only for structural tokens
40104                // (CLUSTERED, NONCLUSTERED, INDEX) but not data types (VARCHAR, INT, etc.)
40105                || (t.token_type == TokenType::LParen
40106                    && prev_token_text.as_ref().map_or(false, |text| {
40107                        let upper = text.to_ascii_uppercase();
40108                        matches!(upper.as_str(),
40109                            "CLUSTERED" | "NONCLUSTERED" | "HASH" | "RANGE"
40110                            | "INCLUDE" | "FILLFACTOR" | "PAD_INDEX"
40111                        )
40112                    }));
40113            let needs_space = !result.is_empty()
40114                && prev_token_type != Some(TokenType::LParen)
40115                && prev_token_type != Some(TokenType::Dot)
40116                && t.token_type != TokenType::Comma
40117                && t.token_type != TokenType::RParen
40118                && t.token_type != TokenType::Dot
40119                && (t.token_type != TokenType::LParen || is_lparen_after_keyword);
40120
40121            // Add space after comma
40122            if prev_token_type == Some(TokenType::Comma) {
40123                result.push(' ');
40124            } else if needs_space {
40125                result.push(' ');
40126            }
40127
40128            if t.token_type == TokenType::String {
40129                // Re-add quotes around string literals
40130                result.push('\'');
40131                result.push_str(&t.text.replace('\'', "''"));
40132                result.push('\'');
40133            } else if t.token_type.is_keyword() {
40134                // Uppercase keyword tokens
40135                result.push_str(&t.text.to_ascii_uppercase());
40136            } else {
40137                // For non-keyword tokens, preserve original text
40138                result.push_str(&t.text);
40139            }
40140
40141            prev_token_type = Some(t.token_type);
40142            prev_token_text = Some(t.text.clone());
40143        }
40144        result
40145    }
40146
40147    /// Check if current token matches type
40148    #[inline]
40149    fn check(&self, token_type: TokenType) -> bool {
40150        if self.is_at_end() {
40151            false
40152        } else {
40153            self.peek().token_type == token_type
40154        }
40155    }
40156
40157    /// Check if current token is a keyword
40158    fn check_keyword(&self) -> bool {
40159        if self.is_at_end() {
40160            false
40161        } else {
40162            self.peek().token_type.is_keyword()
40163        }
40164    }
40165
40166    /// Check if current UNPIVOT token starts an UNPIVOT clause (vs being an alias).
40167    /// UNPIVOT clause starts with: UNPIVOT(, UNPIVOT INCLUDE, or UNPIVOT EXCLUDE
40168    fn is_unpivot_clause_start(&self) -> bool {
40169        if !self.check(TokenType::Unpivot) {
40170            return false;
40171        }
40172        let next_idx = self.current + 1;
40173        if next_idx >= self.tokens.len() {
40174            return false;
40175        }
40176        let next = &self.tokens[next_idx];
40177        if next.token_type == TokenType::LParen {
40178            return true;
40179        }
40180        // UNPIVOT INCLUDE NULLS (...) or UNPIVOT EXCLUDE NULLS (...)
40181        let next_text = next.text.to_ascii_uppercase();
40182        next_text == "INCLUDE" || next_text == "EXCLUDE"
40183    }
40184
40185    /// Check if current token text matches (case-insensitive), does not advance
40186    fn check_keyword_text(&self, keyword: &str) -> bool {
40187        if self.is_at_end() {
40188            false
40189        } else {
40190            self.peek().text.eq_ignore_ascii_case(keyword)
40191        }
40192    }
40193
40194    /// Check if current token is FROM keyword
40195    fn check_from_keyword(&self) -> bool {
40196        self.check(TokenType::From)
40197    }
40198
40199    /// Check if next token matches type
40200    fn check_next(&self, token_type: TokenType) -> bool {
40201        if self.current + 1 >= self.tokens.len() {
40202            false
40203        } else {
40204            self.tokens[self.current + 1].token_type == token_type
40205        }
40206    }
40207
40208    /// Check if next token is an identifier with specific name (case-insensitive)
40209    fn check_next_identifier(&self, name: &str) -> bool {
40210        if self.current + 1 >= self.tokens.len() {
40211            false
40212        } else {
40213            let token = &self.tokens[self.current + 1];
40214            (token.token_type == TokenType::Var || token.token_type == TokenType::Identifier)
40215                && token.text.eq_ignore_ascii_case(name)
40216        }
40217    }
40218
40219    /// Match an identifier with specific text (case insensitive)
40220    /// Checks for Identifier, Var, and QuotedIdentifier tokens
40221    fn match_identifier(&mut self, text: &str) -> bool {
40222        if (self.check(TokenType::Identifier)
40223            || self.check(TokenType::Var)
40224            || self.check(TokenType::QuotedIdentifier))
40225            && self.peek().text.eq_ignore_ascii_case(text)
40226        {
40227            self.skip();
40228            true
40229        } else {
40230            false
40231        }
40232    }
40233
40234    /// Check if current token is an identifier with specific text (case insensitive)
40235    /// Does NOT advance the parser
40236    fn check_identifier(&self, text: &str) -> bool {
40237        if self.is_at_end() {
40238            return false;
40239        }
40240        (self.check(TokenType::Identifier)
40241            || self.check(TokenType::Var)
40242            || self.check(TokenType::QuotedIdentifier))
40243            && self.peek().text.eq_ignore_ascii_case(text)
40244    }
40245
40246    /// Check if current token is a "safe" keyword that can be used as an identifier.
40247    /// Check if the current Percent token is a PERCENT modifier (not a modulo operator).
40248    /// "PERCENT" spelled out is always a modifier. "%" is a modifier when followed by
40249    /// a clause boundary (OFFSET, end of input, semicolon, RParen, comma, etc.)
40250    fn is_percent_modifier(&self) -> bool {
40251        if self.is_at_end() {
40252            return false;
40253        }
40254        if self.peek().text.eq_ignore_ascii_case("PERCENT") {
40255            return true;
40256        }
40257        // "%" symbol — only treat as PERCENT modifier if followed by a boundary
40258        if self.peek().text == "%" {
40259            let next_idx = self.current + 1;
40260            if next_idx >= self.tokens.len() {
40261                return true; // at end — it's PERCENT
40262            }
40263            let next_type = self.tokens[next_idx].token_type;
40264            return matches!(
40265                next_type,
40266                TokenType::Offset
40267                    | TokenType::Semicolon
40268                    | TokenType::RParen
40269                    | TokenType::From
40270                    | TokenType::Where
40271                    | TokenType::GroupBy
40272                    | TokenType::OrderBy
40273                    | TokenType::Having
40274                    | TokenType::Union
40275                    | TokenType::Intersect
40276                    | TokenType::Except
40277                    | TokenType::Comma
40278                    | TokenType::With // WITH TIES
40279            ) || next_idx >= self.tokens.len();
40280        }
40281        false
40282    }
40283
40284    /// Structural keywords like FROM, WHERE, JOIN, SELECT are NOT safe.
40285    /// Non-structural keywords like FILTER, UPDATE, END, VALUES can be used as identifiers.
40286    fn is_safe_keyword_as_identifier(&self) -> bool {
40287        if self.is_at_end() {
40288            return false;
40289        }
40290        let token_type = self.peek().token_type;
40291        // Structural keywords that should NOT be used as identifiers
40292        let is_structural = matches!(
40293            token_type,
40294            TokenType::From
40295                | TokenType::Where
40296                | TokenType::Select
40297                | TokenType::Insert
40298                | TokenType::Delete
40299                | TokenType::Create
40300                | TokenType::Drop
40301                | TokenType::Alter
40302                | TokenType::Join
40303                | TokenType::Inner
40304                | TokenType::Cross
40305                | TokenType::On
40306                | TokenType::GroupBy
40307                | TokenType::OrderBy
40308                | TokenType::Having
40309                | TokenType::With
40310                | TokenType::Union
40311                | TokenType::Intersect
40312                | TokenType::Except
40313                | TokenType::Qualify
40314                | TokenType::Into
40315                | TokenType::Set
40316                | TokenType::Using
40317                | TokenType::Lateral
40318                | TokenType::Natural
40319        );
40320        // ClickHouse allows many SQL keywords as identifiers (table names, column aliases, etc.)
40321        if matches!(
40322            self.config.dialect,
40323            Some(crate::dialects::DialectType::ClickHouse)
40324        ) {
40325            let is_ch_structural = matches!(
40326                token_type,
40327                TokenType::From
40328                    | TokenType::Where
40329                    | TokenType::Select
40330                    | TokenType::Create
40331                    | TokenType::Drop
40332                    | TokenType::Alter
40333                    | TokenType::On
40334                    | TokenType::GroupBy
40335                    | TokenType::OrderBy
40336                    | TokenType::Having
40337                    | TokenType::With
40338                    | TokenType::Union
40339                    | TokenType::Intersect
40340                    | TokenType::Except
40341                    | TokenType::Into
40342                    | TokenType::Using
40343                    | TokenType::Lateral
40344                    | TokenType::Natural
40345            );
40346            // Also allow certain operator tokens and non-keyword tokens as identifiers
40347            if matches!(token_type, TokenType::RLike | TokenType::Values) {
40348                return true;
40349            }
40350            return self.peek().token_type.is_keyword() && !is_ch_structural;
40351        }
40352        // If it's a keyword but NOT structural, it's safe to use as identifier
40353        self.peek().token_type.is_keyword() && !is_structural
40354    }
40355
40356    /// Check if a token at current position is the last meaningful token in an expression context.
40357    /// This is used to detect when a keyword like IS or KEEP should be treated as an alias
40358    /// instead of an operator keyword.
40359    fn is_last_expression_token(&self, _token_type: TokenType) -> bool {
40360        // Check if the token after the current one is end-of-input or a clause boundary
40361        let next_idx = self.current + 1;
40362        if next_idx >= self.tokens.len() {
40363            return true; // at end of input
40364        }
40365        let next_type = self.tokens[next_idx].token_type;
40366        // Clause boundaries that indicate the current token is the last in the expression
40367        matches!(
40368            next_type,
40369            TokenType::From
40370                | TokenType::Where
40371                | TokenType::GroupBy
40372                | TokenType::OrderBy
40373                | TokenType::Having
40374                | TokenType::Limit
40375                | TokenType::Union
40376                | TokenType::Intersect
40377                | TokenType::Except
40378                | TokenType::Semicolon
40379                | TokenType::RParen
40380                | TokenType::Comma
40381        )
40382    }
40383
40384    /// Check if current token is a type keyword (for lambda type annotations)
40385    fn is_type_keyword(&self) -> bool {
40386        if self.is_at_end() {
40387            return false;
40388        }
40389        let token = self.peek();
40390        // Check for common type keywords that might appear in lambda annotations
40391        // Use text comparison to avoid depending on specific TokenType variants
40392        let text_upper = token.text.to_ascii_uppercase();
40393        matches!(
40394            text_upper.as_str(),
40395            "INT"
40396                | "INTEGER"
40397                | "BIGINT"
40398                | "SMALLINT"
40399                | "TINYINT"
40400                | "DOUBLE"
40401                | "FLOAT"
40402                | "DECIMAL"
40403                | "NUMERIC"
40404                | "REAL"
40405                | "VARCHAR"
40406                | "CHAR"
40407                | "TEXT"
40408                | "STRING"
40409                | "NVARCHAR"
40410                | "NCHAR"
40411                | "BOOLEAN"
40412                | "BOOL"
40413                | "DATE"
40414                | "TIME"
40415                | "TIMESTAMP"
40416                | "DATETIME"
40417                | "INTERVAL"
40418                | "BINARY"
40419                | "VARBINARY"
40420                | "BLOB"
40421                | "ARRAY"
40422                | "MAP"
40423                | "STRUCT"
40424                | "OBJECT"
40425                | "VARIANT"
40426                | "JSON"
40427                | "NUMBER"
40428                | "VARCHAR2"
40429        )
40430    }
40431
40432    /// Check if current token is a command keyword that can safely be used as an implicit alias.
40433    /// This is a narrow set of command-like keywords (GET, PUT, COPY, SHOW, etc.) that are
40434    /// unlikely to conflict with SQL clause keywords when used as implicit aliases.
40435    fn is_command_keyword_as_alias(&self) -> bool {
40436        if self.is_at_end() {
40437            return false;
40438        }
40439        let token_type = self.peek().token_type;
40440        // FORMAT is a query modifier in ClickHouse, so don't treat it as an alias there
40441        if matches!(token_type, TokenType::Format) {
40442            return !matches!(
40443                self.config.dialect,
40444                Some(crate::dialects::DialectType::ClickHouse)
40445            );
40446        }
40447        // Base keywords that can be aliases in all dialects
40448        if matches!(
40449            token_type,
40450            TokenType::Get
40451                | TokenType::Put
40452                | TokenType::Copy
40453                | TokenType::Show
40454                | TokenType::Rename
40455                | TokenType::Enum
40456                | TokenType::Sample
40457                | TokenType::Collate
40458                | TokenType::Add
40459        ) {
40460            return true;
40461        }
40462        // Spark/Hive allow LIMIT and OFFSET as aliases (without quoting),
40463        // but only when NOT followed by a number/expression (which means it's the actual clause)
40464        if matches!(
40465            self.config.dialect,
40466            Some(crate::dialects::DialectType::Spark)
40467                | Some(crate::dialects::DialectType::Hive)
40468                | Some(crate::dialects::DialectType::Databricks)
40469        ) && matches!(token_type, TokenType::Limit | TokenType::Offset)
40470        {
40471            let next = self.current + 1;
40472            let next_is_value = next < self.tokens.len()
40473                && matches!(
40474                    self.tokens[next].token_type,
40475                    TokenType::Number
40476                        | TokenType::LParen
40477                        | TokenType::Var
40478                        | TokenType::Parameter
40479                        | TokenType::All
40480                );
40481            if !next_is_value {
40482                return true;
40483            }
40484        }
40485        false
40486    }
40487
40488    /// Check if current token is a keyword that can be used as a table alias.
40489    /// This is more permissive than is_safe_keyword_as_identifier - it allows
40490    /// LEFT, RIGHT, OUTER, FULL which are JOIN keywords but can also be aliases.
40491    fn can_be_alias_keyword(&self) -> bool {
40492        if self.is_at_end() {
40493            return false;
40494        }
40495        let token_type = self.peek().token_type;
40496        // Keywords that can be used as aliases (similar to is_safe_keyword but more permissive)
40497        matches!(
40498            token_type,
40499            TokenType::Left
40500                | TokenType::Right
40501                | TokenType::Outer
40502                | TokenType::Full
40503                | TokenType::Only
40504                | TokenType::Next
40505                | TokenType::All
40506                | TokenType::If
40507        ) || self.is_safe_keyword_as_identifier()
40508    }
40509
40510    /// Match and consume a token type
40511    fn match_token(&mut self, token_type: TokenType) -> bool {
40512        if self.check(token_type) {
40513            self.skip();
40514            true
40515        } else {
40516            false
40517        }
40518    }
40519
40520    /// Match a sequence of keywords
40521    fn match_keywords(&mut self, keywords: &[TokenType]) -> bool {
40522        // Check if all keywords match
40523        for (i, &kw) in keywords.iter().enumerate() {
40524            if self.current + i >= self.tokens.len() {
40525                return false;
40526            }
40527            if self.tokens[self.current + i].token_type != kw {
40528                return false;
40529            }
40530        }
40531
40532        // Consume all matched keywords
40533        self.current += keywords.len();
40534        true
40535    }
40536
40537    /// Expect a specific token type
40538    fn expect(&mut self, token_type: TokenType) -> Result<Token> {
40539        if self.check(token_type) {
40540            Ok(self.advance())
40541        } else {
40542            let got = if self.is_at_end() {
40543                "end of input".to_string()
40544            } else {
40545                format!("{:?}", self.peek().token_type)
40546            };
40547            let got_text = if self.is_at_end() {
40548                "".to_string()
40549            } else {
40550                self.peek().text.clone()
40551            };
40552            let start = self.current.saturating_sub(3);
40553            let end = (self.current + 4).min(self.tokens.len());
40554            let context = self.tokens_to_sql(start, end).replace('\n', " ");
40555            Err(self.parse_error(format!(
40556                "Expected {:?}, got {} ('{}') near [{}]",
40557                token_type, got, got_text, context
40558            )))
40559        }
40560    }
40561
40562    /// Expect a `>` token, handling the case where `>>` was tokenized as GtGt
40563    /// This is needed for parsing nested generic types like `ARRAY<ARRAY<INT>>`
40564    fn expect_gt(&mut self) -> Result<Token> {
40565        if self.check(TokenType::Gt) {
40566            Ok(self.advance())
40567        } else if self.check(TokenType::GtGt) {
40568            // Split >> into two > tokens
40569            // Replace the GtGt with Gt and return a synthetic Gt token
40570            let token = self.peek().clone();
40571            self.tokens[self.current] = Token {
40572                token_type: TokenType::Gt,
40573                text: ">".to_string(),
40574                span: Span {
40575                    start: token.span.start + 1,
40576                    end: token.span.end,
40577                    line: token.span.line,
40578                    column: token.span.column + 1,
40579                },
40580                comments: Vec::new(),
40581                trailing_comments: Vec::new(),
40582            };
40583            Ok(Token {
40584                token_type: TokenType::Gt,
40585                text: ">".to_string(),
40586                span: Span {
40587                    start: token.span.start,
40588                    end: token.span.start + 1,
40589                    line: token.span.line,
40590                    column: token.span.column,
40591                },
40592                comments: token.comments,
40593                trailing_comments: Vec::new(),
40594            })
40595        } else {
40596            Err(self.parse_error(format!(
40597                "Expected Gt, got {:?}",
40598                if self.is_at_end() {
40599                    "end of input".to_string()
40600                } else {
40601                    format!("{:?}", self.peek().token_type)
40602                }
40603            )))
40604        }
40605    }
40606
40607    /// Expect a string literal and return its value
40608    fn expect_string(&mut self) -> Result<String> {
40609        if self.check(TokenType::String) || self.check(TokenType::DollarString) {
40610            Ok(self.advance().text)
40611        } else {
40612            Err(self.parse_error(format!(
40613                "Expected string, got {:?}",
40614                if self.is_at_end() {
40615                    "end of input".to_string()
40616                } else {
40617                    format!("{:?}", self.peek().token_type)
40618                }
40619            )))
40620        }
40621    }
40622
40623    /// Check if the current token is any kind of identifier (regular, quoted, or var)
40624    fn is_identifier_token(&self) -> bool {
40625        self.check(TokenType::Var)
40626            || self.check(TokenType::Identifier)
40627            || self.check(TokenType::QuotedIdentifier)
40628    }
40629
40630    /// Check if current token is a stage reference (starts with @)
40631    /// This handles both DAt token and Var tokens that start with @
40632    fn is_stage_reference(&self) -> bool {
40633        self.check(TokenType::DAt)
40634            || (self.check(TokenType::Var) && self.peek().text.starts_with('@'))
40635    }
40636
40637    /// Check if the current token could be a MySQL numeric-starting identifier (e.g., 00f, 1d)
40638    /// This checks that the Number token is followed by a connected Var/Identifier token
40639    fn is_mysql_numeric_identifier(&self) -> bool {
40640        if !self.check(TokenType::Number)
40641            || !matches!(
40642                self.config.dialect,
40643                Some(crate::dialects::DialectType::MySQL)
40644            )
40645        {
40646            return false;
40647        }
40648        // Check if the next token is connected (no space) and is a var/identifier
40649        if self.current + 1 < self.tokens.len() {
40650            let curr = &self.tokens[self.current];
40651            let next = &self.tokens[self.current + 1];
40652            // Tokens are connected if they are immediately adjacent (no whitespace between)
40653            // span.end is exclusive, so if curr.end == next.start, they are adjacent
40654            let connected = curr.span.end == next.span.start;
40655            connected
40656                && (next.token_type == TokenType::Var || next.token_type == TokenType::Identifier)
40657        } else {
40658            false
40659        }
40660    }
40661
40662    /// Parse a MySQL numeric-starting identifier (e.g., 00f, 1d)
40663    /// Merges the number token with connected identifier tokens
40664    fn parse_mysql_numeric_identifier(&mut self) -> Identifier {
40665        let num_token = self.advance();
40666        let mut name = num_token.text.clone();
40667        // Merge with connected identifier/var tokens
40668        while !self.is_at_end()
40669            && self.is_connected()
40670            && (self.check(TokenType::Var) || self.check(TokenType::Identifier))
40671        {
40672            let tok = self.advance();
40673            name.push_str(&tok.text);
40674        }
40675        Identifier {
40676            name,
40677            // sqlglot treats this as an identifier token and re-emits it quoted.
40678            quoted: true,
40679            trailing_comments: Vec::new(),
40680            span: None,
40681        }
40682    }
40683
40684    /// Check if an uppercase string starting with '_' is a MySQL charset introducer
40685    fn is_mysql_charset_introducer(text: &str) -> bool {
40686        matches!(
40687            text,
40688            "_ARMSCII8"
40689                | "_ASCII"
40690                | "_BIG5"
40691                | "_BINARY"
40692                | "_CP1250"
40693                | "_CP1251"
40694                | "_CP1256"
40695                | "_CP1257"
40696                | "_CP850"
40697                | "_CP852"
40698                | "_CP866"
40699                | "_CP932"
40700                | "_DEC8"
40701                | "_EUCJPMS"
40702                | "_EUCKR"
40703                | "_GB18030"
40704                | "_GB2312"
40705                | "_GBK"
40706                | "_GEOSTD8"
40707                | "_GREEK"
40708                | "_HEBREW"
40709                | "_HP8"
40710                | "_KEYBCS2"
40711                | "_KOI8R"
40712                | "_KOI8U"
40713                | "_LATIN1"
40714                | "_LATIN2"
40715                | "_LATIN5"
40716                | "_LATIN7"
40717                | "_MACCE"
40718                | "_MACROMAN"
40719                | "_SJIS"
40720                | "_SWE7"
40721                | "_TIS620"
40722                | "_UCS2"
40723                | "_UJIS"
40724                | "_UTF8"
40725                | "_UTF16"
40726                | "_UTF16LE"
40727                | "_UTF32"
40728                | "_UTF8MB3"
40729                | "_UTF8MB4"
40730        )
40731    }
40732
40733    /// Check if the current token can be used as an identifier (includes keywords)
40734    fn is_identifier_or_keyword_token(&self) -> bool {
40735        self.is_identifier_token() || self.check_keyword()
40736    }
40737
40738    /// Expect an identifier and return an Identifier struct with quoted flag
40739    fn expect_identifier_with_quoted(&mut self) -> Result<Identifier> {
40740        if self.is_mysql_numeric_identifier() {
40741            return Ok(self.parse_mysql_numeric_identifier());
40742        }
40743        if self.is_identifier_token() {
40744            let token = self.advance();
40745            let quoted = token.token_type == TokenType::QuotedIdentifier;
40746            Ok(Identifier {
40747                name: token.text,
40748                quoted,
40749                trailing_comments: Vec::new(),
40750                span: None,
40751            })
40752        } else if self.check(TokenType::LBrace)
40753            && matches!(
40754                self.config.dialect,
40755                Some(crate::dialects::DialectType::ClickHouse)
40756            )
40757        {
40758            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40759                if let Expression::Parameter(param) = &param_expr {
40760                    let name = format!(
40761                        "{{{}: {}}}",
40762                        param.name.as_deref().unwrap_or(""),
40763                        param.expression.as_deref().unwrap_or("")
40764                    );
40765                    return Ok(Identifier {
40766                        name,
40767                        quoted: false,
40768                        trailing_comments: Vec::new(),
40769                        span: None,
40770                    });
40771                }
40772            }
40773            Err(self.parse_error("Expected identifier, got LBrace"))
40774        } else {
40775            Err(self.parse_error(format!(
40776                "Expected identifier, got {:?}",
40777                if self.is_at_end() {
40778                    "end of input".to_string()
40779                } else {
40780                    format!("{:?}", self.peek().token_type)
40781                }
40782            )))
40783        }
40784    }
40785
40786    /// Parse a possibly dot-qualified identifier into parts (e.g. "mydb.hr" → [mydb, hr]).
40787    fn parse_identifier_parts(&mut self) -> Result<Vec<Identifier>> {
40788        let first = self.expect_identifier_with_quoted()?;
40789        let mut parts = vec![first];
40790        while self.match_token(TokenType::Dot) {
40791            parts.push(self.expect_identifier_with_quoted()?);
40792        }
40793        Ok(parts)
40794    }
40795
40796    /// Expect an identifier or keyword (for column names, field names, etc.)
40797    fn expect_identifier_or_keyword_with_quoted(&mut self) -> Result<Identifier> {
40798        // MySQL numeric-starting identifiers (e.g., 00f, 1d)
40799        if self.is_mysql_numeric_identifier() {
40800            return Ok(self.parse_mysql_numeric_identifier());
40801        }
40802        // Also accept ? (Parameter) as an identifier placeholder
40803        // For positional parameters like $23, the token text is "23" (without $)
40804        if self.check(TokenType::Parameter) {
40805            let token = self.advance();
40806            // If the text is a number, it's a positional parameter like $1, $2, $23
40807            // Construct $N as the identifier name
40808            let name = if token.text.chars().all(|c| c.is_ascii_digit()) && !token.text.is_empty() {
40809                format!("${}", token.text)
40810            } else {
40811                // Plain ? placeholder or other parameter
40812                "?".to_string()
40813            };
40814            return Ok(Identifier {
40815                name,
40816                quoted: false,
40817                trailing_comments: Vec::new(),
40818                span: None,
40819            });
40820        }
40821        if self.is_identifier_or_keyword_token() {
40822            let token = self.advance();
40823            let quoted = token.token_type == TokenType::QuotedIdentifier;
40824            Ok(Identifier {
40825                name: token.text,
40826                quoted,
40827                trailing_comments: Vec::new(),
40828                span: None,
40829            })
40830        } else if self.check(TokenType::LBrace)
40831            && matches!(
40832                self.config.dialect,
40833                Some(crate::dialects::DialectType::ClickHouse)
40834            )
40835        {
40836            // ClickHouse query parameter: {name:Type}
40837            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40838                // Extract the parameter name to use as the identifier
40839                if let Expression::Parameter(param) = &param_expr {
40840                    let name = format!(
40841                        "{{{}: {}}}",
40842                        param.name.as_deref().unwrap_or(""),
40843                        param.expression.as_deref().unwrap_or("")
40844                    );
40845                    return Ok(Identifier {
40846                        name,
40847                        quoted: false,
40848                        trailing_comments: Vec::new(),
40849                        span: None,
40850                    });
40851                }
40852            }
40853            Err(self.parse_error("Expected identifier, got LBrace"))
40854        } else {
40855            Err(self.parse_error(format!(
40856                "Expected identifier, got {:?}",
40857                if self.is_at_end() {
40858                    "end of input".to_string()
40859                } else {
40860                    format!("{:?}", self.peek().token_type)
40861                }
40862            )))
40863        }
40864    }
40865
40866    /// Expect an identifier
40867    fn expect_identifier(&mut self) -> Result<String> {
40868        if self.is_identifier_token() {
40869            Ok(self.advance().text)
40870        } else if self.check(TokenType::LBrace)
40871            && matches!(
40872                self.config.dialect,
40873                Some(crate::dialects::DialectType::ClickHouse)
40874            )
40875        {
40876            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40877                if let Expression::Parameter(param) = &param_expr {
40878                    return Ok(format!(
40879                        "{{{}: {}}}",
40880                        param.name.as_deref().unwrap_or(""),
40881                        param.expression.as_deref().unwrap_or("")
40882                    ));
40883                }
40884            }
40885            Err(self.parse_error("Expected identifier, got LBrace"))
40886        } else {
40887            Err(self.parse_error(format!(
40888                "Expected identifier, got {:?}",
40889                if self.is_at_end() {
40890                    "end of input".to_string()
40891                } else {
40892                    format!("{:?}", self.peek().token_type)
40893                }
40894            )))
40895        }
40896    }
40897
40898    /// Expect an identifier or keyword (for aliases, column names, etc.)
40899    fn expect_identifier_or_keyword(&mut self) -> Result<String> {
40900        if self.is_identifier_or_keyword_token() {
40901            Ok(self.advance().text)
40902        } else if self.check(TokenType::LBrace)
40903            && matches!(
40904                self.config.dialect,
40905                Some(crate::dialects::DialectType::ClickHouse)
40906            )
40907        {
40908            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40909                if let Expression::Parameter(param) = &param_expr {
40910                    return Ok(format!(
40911                        "{{{}: {}}}",
40912                        param.name.as_deref().unwrap_or(""),
40913                        param.expression.as_deref().unwrap_or("")
40914                    ));
40915                }
40916            }
40917            Err(self.parse_error("Expected identifier, got LBrace"))
40918        } else {
40919            Err(self.parse_error(format!(
40920                "Expected identifier, got {:?}",
40921                if self.is_at_end() {
40922                    "end of input".to_string()
40923                } else {
40924                    format!("{:?}", self.peek().token_type)
40925                }
40926            )))
40927        }
40928    }
40929
40930    /// Expect an identifier or safe keyword (for CTE names, column names in CREATE TABLE, etc.)
40931    /// This is more permissive than expect_identifier but excludes structural keywords
40932    fn expect_identifier_or_safe_keyword(&mut self) -> Result<String> {
40933        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
40934            Ok(self.advance().text)
40935        } else if self.check(TokenType::LBrace)
40936            && matches!(
40937                self.config.dialect,
40938                Some(crate::dialects::DialectType::ClickHouse)
40939            )
40940        {
40941            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40942                if let Expression::Parameter(param) = &param_expr {
40943                    return Ok(format!(
40944                        "{{{}: {}}}",
40945                        param.name.as_deref().unwrap_or(""),
40946                        param.expression.as_deref().unwrap_or("")
40947                    ));
40948                }
40949            }
40950            Err(self.parse_error("Expected identifier, got LBrace"))
40951        } else {
40952            Err(self.parse_error(format!(
40953                "Expected identifier, got {:?}",
40954                if self.is_at_end() {
40955                    "end of input".to_string()
40956                } else {
40957                    format!("{:?}", self.peek().token_type)
40958                }
40959            )))
40960        }
40961    }
40962
40963    /// Expect an identifier or safe keyword, preserving quoted flag
40964    fn expect_identifier_or_safe_keyword_with_quoted(&mut self) -> Result<Identifier> {
40965        if self.is_mysql_numeric_identifier() {
40966            return Ok(self.parse_mysql_numeric_identifier());
40967        }
40968        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
40969            let token = self.advance();
40970            let quoted = token.token_type == TokenType::QuotedIdentifier;
40971            Ok(Identifier {
40972                name: token.text,
40973                quoted,
40974                trailing_comments: Vec::new(),
40975                span: None,
40976            })
40977        } else {
40978            Err(self.parse_error(format!(
40979                "Expected identifier, got {:?}",
40980                if self.is_at_end() {
40981                    "end of input".to_string()
40982                } else {
40983                    format!("{:?}", self.peek().token_type)
40984                }
40985            )))
40986        }
40987    }
40988
40989    fn expect_identifier_or_alias_keyword_with_quoted(&mut self) -> Result<Identifier> {
40990        // ClickHouse: any keyword can be used as a table alias after explicit AS
40991        let ch_keyword = matches!(
40992            self.config.dialect,
40993            Some(crate::dialects::DialectType::ClickHouse)
40994        ) && self.peek().token_type.is_keyword();
40995        if self.is_identifier_token()
40996            || self.can_be_alias_keyword()
40997            || self.is_safe_keyword_as_identifier()
40998            || ch_keyword
40999        {
41000            let token = self.advance();
41001            let quoted = token.token_type == TokenType::QuotedIdentifier;
41002            Ok(Identifier {
41003                name: token.text,
41004                quoted,
41005                trailing_comments: Vec::new(),
41006                span: None,
41007            })
41008        } else if self.check(TokenType::String)
41009            && matches!(
41010                self.config.dialect,
41011                Some(crate::dialects::DialectType::DuckDB)
41012            )
41013        {
41014            // DuckDB allows string literals as identifiers (e.g., WITH 'x' AS (...))
41015            let token = self.advance();
41016            Ok(Identifier {
41017                name: token.text,
41018                quoted: true,
41019                trailing_comments: Vec::new(),
41020                span: None,
41021            })
41022        } else {
41023            Err(self.parse_error(format!(
41024                "Expected identifier, got {:?}",
41025                if self.is_at_end() {
41026                    "end of input".to_string()
41027                } else {
41028                    format!("{:?}", self.peek().token_type)
41029                }
41030            )))
41031        }
41032    }
41033
41034    /// Expect a number
41035    fn expect_number(&mut self) -> Result<i64> {
41036        let negative = self.match_token(TokenType::Dash);
41037        if self.check(TokenType::Number) {
41038            let text = self.advance().text;
41039            let val = text
41040                .parse::<i64>()
41041                .map_err(|_| self.parse_error(format!("Invalid number: {}", text)))?;
41042            Ok(if negative { -val } else { val })
41043        } else {
41044            Err(self.parse_error("Expected number"))
41045        }
41046    }
41047
41048    /// Parse a comma-separated list of expressions.
41049    /// Supports named arguments with => or := syntax.
41050    fn parse_expression_list_with_capacity(
41051        &mut self,
41052        capacity_hint: usize,
41053    ) -> Result<Vec<Expression>> {
41054        let mut expressions = Vec::with_capacity(capacity_hint);
41055
41056        loop {
41057            // Check if this is a named argument: identifier => value or identifier := value
41058            // Also check for safe keywords (like TYPE, FORMAT, etc.) that can be used as named arg names
41059            let expr = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
41060                let start_pos = self.current;
41061                let name = self.expect_identifier_or_keyword_with_quoted()?;
41062
41063                if self.match_token(TokenType::FArrow) {
41064                    // name => value
41065                    let value = self.parse_expression()?;
41066                    Expression::NamedArgument(Box::new(NamedArgument {
41067                        name,
41068                        value,
41069                        separator: NamedArgSeparator::DArrow,
41070                    }))
41071                } else if self.match_token(TokenType::ColonEq) {
41072                    // name := value
41073                    let value = self.parse_expression()?;
41074                    Expression::NamedArgument(Box::new(NamedArgument {
41075                        name,
41076                        value,
41077                        separator: NamedArgSeparator::ColonEq,
41078                    }))
41079                } else {
41080                    // Not a named argument, backtrack and parse as regular expression
41081                    self.current = start_pos;
41082                    self.parse_expression()?
41083                }
41084            } else {
41085                self.parse_expression()?
41086            };
41087
41088            // Check for AS alias on this expression (Spark/Hive: IF(cond, val AS name, ...))
41089            let expr = if self.check(TokenType::As) {
41090                let as_pos = self.current;
41091                self.skip(); // consume AS
41092                             // Check if what follows looks like an alias name
41093                if self.is_identifier_token()
41094                    || self.is_safe_keyword_as_identifier()
41095                    || (matches!(
41096                        self.config.dialect,
41097                        Some(crate::dialects::DialectType::ClickHouse)
41098                    ) && self.peek().token_type.is_keyword())
41099                {
41100                    let alias = self.expect_identifier_or_keyword_with_quoted()?;
41101                    let alias_expr = Expression::Alias(Box::new(Alias {
41102                        this: expr,
41103                        alias,
41104                        column_aliases: Vec::new(),
41105                        pre_alias_comments: Vec::new(),
41106                        trailing_comments: Vec::new(),
41107                        inferred_type: None,
41108                    }));
41109                    // ClickHouse: if followed by an operator, the alias is part of a bigger expression
41110                    // e.g., blockSize() AS bs < 1000 means (blockSize() AS bs) < 1000
41111                    if matches!(
41112                        self.config.dialect,
41113                        Some(crate::dialects::DialectType::ClickHouse)
41114                    ) && matches!(
41115                        self.peek().token_type,
41116                        TokenType::Lt
41117                            | TokenType::Gt
41118                            | TokenType::Lte
41119                            | TokenType::Gte
41120                            | TokenType::Eq
41121                            | TokenType::Neq
41122                            | TokenType::Plus
41123                            | TokenType::Dash
41124                            | TokenType::Star
41125                            | TokenType::Slash
41126                            | TokenType::Percent
41127                            | TokenType::And
41128                            | TokenType::Or
41129                            | TokenType::Like
41130                            | TokenType::Not
41131                            | TokenType::In
41132                            | TokenType::Is
41133                            | TokenType::Between
41134                    ) {
41135                        // Parse the operator and right-hand side
41136                        let op_token = self.advance();
41137                        let right = self.parse_expression()?;
41138                        match op_token.token_type {
41139                            TokenType::Lt => {
41140                                Expression::Lt(Box::new(BinaryOp::new(alias_expr, right)))
41141                            }
41142                            TokenType::Gt => {
41143                                Expression::Gt(Box::new(BinaryOp::new(alias_expr, right)))
41144                            }
41145                            TokenType::Lte => {
41146                                Expression::Lte(Box::new(BinaryOp::new(alias_expr, right)))
41147                            }
41148                            TokenType::Gte => {
41149                                Expression::Gte(Box::new(BinaryOp::new(alias_expr, right)))
41150                            }
41151                            TokenType::Eq => {
41152                                Expression::Eq(Box::new(BinaryOp::new(alias_expr, right)))
41153                            }
41154                            TokenType::Neq => {
41155                                Expression::Neq(Box::new(BinaryOp::new(alias_expr, right)))
41156                            }
41157                            TokenType::Plus => {
41158                                Expression::Add(Box::new(BinaryOp::new(alias_expr, right)))
41159                            }
41160                            TokenType::Dash => {
41161                                Expression::Sub(Box::new(BinaryOp::new(alias_expr, right)))
41162                            }
41163                            TokenType::Star => {
41164                                Expression::Mul(Box::new(BinaryOp::new(alias_expr, right)))
41165                            }
41166                            TokenType::Slash => {
41167                                Expression::Div(Box::new(BinaryOp::new(alias_expr, right)))
41168                            }
41169                            TokenType::Percent => {
41170                                Expression::Mod(Box::new(BinaryOp::new(alias_expr, right)))
41171                            }
41172                            TokenType::And => {
41173                                Expression::And(Box::new(BinaryOp::new(alias_expr, right)))
41174                            }
41175                            TokenType::Or => {
41176                                Expression::Or(Box::new(BinaryOp::new(alias_expr, right)))
41177                            }
41178                            _ => alias_expr, // fallback, shouldn't happen
41179                        }
41180                    } else {
41181                        alias_expr
41182                    }
41183                } else {
41184                    // Not an alias name, backtrack
41185                    self.current = as_pos;
41186                    expr
41187                }
41188            } else {
41189                expr
41190            };
41191
41192            // Check for trailing comments on this expression
41193            // Only wrap in Annotated for expression types that don't have their own trailing_comments field
41194            let trailing_comments = self.previous_trailing_comments().to_vec();
41195            let expr = if trailing_comments.is_empty() {
41196                expr
41197            } else {
41198                // Only annotate Literals and other types that don't capture trailing comments
41199                match &expr {
41200                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
41201                        Expression::Annotated(Box::new(Annotated {
41202                            this: expr,
41203                            trailing_comments,
41204                        }))
41205                    }
41206                    // For expressions that already capture trailing_comments, don't double-wrap
41207                    _ => expr,
41208                }
41209            };
41210            expressions.push(expr);
41211
41212            if !self.match_token(TokenType::Comma) {
41213                break;
41214            }
41215            // ClickHouse: allow trailing comma before RParen in expression lists
41216            if matches!(
41217                self.config.dialect,
41218                Some(crate::dialects::DialectType::ClickHouse)
41219            ) && self.check(TokenType::RParen)
41220            {
41221                break;
41222            }
41223        }
41224
41225        Ok(expressions)
41226    }
41227
41228    /// Parse a comma-separated list of expressions.
41229    /// Supports named arguments with => or := syntax.
41230    fn parse_expression_list(&mut self) -> Result<Vec<Expression>> {
41231        self.parse_expression_list_with_capacity(0)
41232    }
41233
41234    /// Estimate top-level expression count until the next unmatched `)`.
41235    ///
41236    /// This is used for pre-allocating comma-separated lists like `IN (...)`
41237    /// to reduce `Vec` growth churn on very large lists.
41238    fn estimate_expression_list_capacity_until_rparen(&self) -> usize {
41239        if self.current >= self.tokens.len() || self.check(TokenType::RParen) {
41240            return 0;
41241        }
41242
41243        let mut idx = self.current;
41244        let mut paren_depth = 0usize;
41245        let mut bracket_depth = 0usize;
41246        let mut brace_depth = 0usize;
41247        let mut commas = 0usize;
41248        let mut has_any_token = false;
41249
41250        while idx < self.tokens.len() {
41251            let token_type = self.tokens[idx].token_type;
41252            match token_type {
41253                TokenType::LParen => paren_depth += 1,
41254                TokenType::RParen => {
41255                    if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 {
41256                        break;
41257                    }
41258                    paren_depth = paren_depth.saturating_sub(1);
41259                }
41260                TokenType::LBracket => bracket_depth += 1,
41261                TokenType::RBracket => bracket_depth = bracket_depth.saturating_sub(1),
41262                TokenType::LBrace => brace_depth += 1,
41263                TokenType::RBrace => brace_depth = brace_depth.saturating_sub(1),
41264                TokenType::Comma if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 => {
41265                    commas += 1;
41266                }
41267                _ => {}
41268            }
41269            has_any_token = true;
41270            idx += 1;
41271        }
41272
41273        if has_any_token {
41274            commas + 1
41275        } else {
41276            0
41277        }
41278    }
41279
41280    /// Parse function arguments with lambda support (for TRANSFORM and similar functions).
41281    /// Handles Snowflake typed lambda syntax: `a int -> a + 1`
41282    fn parse_function_args_with_lambda(&mut self) -> Result<Vec<Expression>> {
41283        let mut expressions = Vec::new();
41284
41285        loop {
41286            // Try to detect typed lambda: identifier type -> body
41287            let expr = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
41288                let saved_pos = self.current;
41289                let ident_token = self.advance();
41290                let ident_name = ident_token.text.clone();
41291
41292                // Check for arrow (simple lambda: a -> body)
41293                if self.match_token(TokenType::Arrow) {
41294                    let body = self.parse_expression()?;
41295                    Expression::Lambda(Box::new(LambdaExpr {
41296                        parameters: vec![Identifier::new(ident_name)],
41297                        body,
41298                        colon: false,
41299                        parameter_types: Vec::new(),
41300                    }))
41301                }
41302                // Check for type annotation followed by arrow: a int -> body
41303                else if !self.is_at_end()
41304                    && self.is_type_keyword()
41305                    && !self.check(TokenType::FArrow)
41306                    && !self.check(TokenType::ColonEq)
41307                {
41308                    let type_annotation = self.parse_data_type()?;
41309                    if self.match_token(TokenType::Arrow) {
41310                        let body = self.parse_expression()?;
41311                        Expression::Lambda(Box::new(LambdaExpr {
41312                            parameters: vec![Identifier::new(ident_name)],
41313                            body,
41314                            colon: false,
41315                            parameter_types: vec![Some(type_annotation)],
41316                        }))
41317                    } else {
41318                        self.current = saved_pos;
41319                        self.parse_expression()?
41320                    }
41321                } else {
41322                    // Not a lambda, backtrack and parse as regular expression
41323                    self.current = saved_pos;
41324                    self.parse_expression()?
41325                }
41326            } else {
41327                self.parse_expression()?
41328            };
41329
41330            expressions.push(expr);
41331            if !self.match_token(TokenType::Comma) {
41332                break;
41333            }
41334        }
41335
41336        Ok(expressions)
41337    }
41338
41339    /// Parse a comma-separated list of expressions for VALUES tuples
41340    /// This variant supports AS aliases on each element (Hive syntax): VALUES (1 AS a, 2 AS b, 3)
41341    fn parse_values_expression_list(&mut self) -> Result<Vec<Expression>> {
41342        let mut expressions = Vec::new();
41343
41344        loop {
41345            // Handle DEFAULT keyword in VALUES - output as unquoted Var (like Python sqlglot's exp.var("DEFAULT"))
41346            let expr = if self.match_token(TokenType::Default) {
41347                Expression::Var(Box::new(crate::expressions::Var {
41348                    this: "DEFAULT".to_string(),
41349                }))
41350            } else {
41351                self.parse_expression()?
41352            };
41353
41354            // Capture trailing comments on the expression (e.g., `1 /* c4 */`)
41355            let trailing_comments = self.previous_trailing_comments().to_vec();
41356            let expr = if !trailing_comments.is_empty() {
41357                match &expr {
41358                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
41359                        Expression::Annotated(Box::new(crate::expressions::Annotated {
41360                            this: expr,
41361                            trailing_comments,
41362                        }))
41363                    }
41364                    _ => expr,
41365                }
41366            } else {
41367                expr
41368            };
41369
41370            // Check for AS alias on this value element (Hive syntax)
41371            let expr_with_alias = if self.match_token(TokenType::As) {
41372                let alias = self.expect_identifier_or_keyword_with_quoted()?;
41373                Expression::Alias(Box::new(Alias::new(expr, alias)))
41374            } else {
41375                expr
41376            };
41377
41378            expressions.push(expr_with_alias);
41379
41380            if !self.match_token(TokenType::Comma) {
41381                break;
41382            }
41383            // ClickHouse: trailing comma in VALUES, e.g., (1, 2, 3,)
41384            if self.check(TokenType::RParen) {
41385                break;
41386            }
41387        }
41388
41389        Ok(expressions)
41390    }
41391
41392    /// Parse a comma-separated list of identifiers
41393    fn parse_identifier_list(&mut self) -> Result<Vec<Identifier>> {
41394        let mut identifiers = Vec::new();
41395
41396        loop {
41397            // Allow keywords as identifiers in identifier lists (e.g., CTE column aliases)
41398            // Check if it's a quoted identifier before consuming
41399            let quoted = self.check(TokenType::QuotedIdentifier);
41400            let mut name = self.expect_identifier_or_safe_keyword()?;
41401            // ClickHouse: handle dotted names in identifier lists (e.g., INSERT INTO t (n.a, n.b))
41402            // Use keyword_with_quoted to allow any keyword after dot (e.g., replace.from)
41403            if matches!(
41404                self.config.dialect,
41405                Some(crate::dialects::DialectType::ClickHouse)
41406            ) {
41407                while self.match_token(TokenType::Dot) {
41408                    let sub_id = self.expect_identifier_or_keyword_with_quoted()?;
41409                    name = format!("{}.{}", name, sub_id.name);
41410                }
41411            }
41412            let trailing_comments = self.previous_trailing_comments().to_vec();
41413            identifiers.push(Identifier {
41414                name,
41415                quoted,
41416                trailing_comments,
41417                span: None,
41418            });
41419
41420            if !self.match_token(TokenType::Comma) {
41421                break;
41422            }
41423            // ClickHouse: allow trailing comma before RParen in identifier lists
41424            if matches!(
41425                self.config.dialect,
41426                Some(crate::dialects::DialectType::ClickHouse)
41427            ) && self.check(TokenType::RParen)
41428            {
41429                break;
41430            }
41431        }
41432
41433        Ok(identifiers)
41434    }
41435
41436    /// Parse a comma-separated list of column references for USING clause
41437    /// Supports qualified names like table.col but extracts only the column part
41438    fn parse_using_column_list(&mut self) -> Result<Vec<Identifier>> {
41439        let mut identifiers = Vec::new();
41440
41441        loop {
41442            // ClickHouse: USING * — wildcard in USING clause
41443            if matches!(
41444                self.config.dialect,
41445                Some(crate::dialects::DialectType::ClickHouse)
41446            ) && self.match_token(TokenType::Star)
41447            {
41448                identifiers.push(Identifier::new("*".to_string()));
41449                if !self.match_token(TokenType::Comma) {
41450                    break;
41451                }
41452                continue;
41453            }
41454            // Check if it's a quoted identifier before consuming
41455            let quoted = self.check(TokenType::QuotedIdentifier);
41456            let mut name = self.expect_identifier_or_safe_keyword()?;
41457            let mut final_quoted = quoted;
41458
41459            // Handle qualified names: table.column or schema.table.column
41460            // Keep only the final column name
41461            while self.match_token(TokenType::Dot) {
41462                final_quoted = self.check(TokenType::QuotedIdentifier);
41463                name = self.expect_identifier_or_safe_keyword()?;
41464            }
41465
41466            // ClickHouse: USING (col AS alias) — consume optional AS alias
41467            if matches!(
41468                self.config.dialect,
41469                Some(crate::dialects::DialectType::ClickHouse)
41470            ) && self.match_token(TokenType::As)
41471            {
41472                // Use the alias name instead
41473                final_quoted = self.check(TokenType::QuotedIdentifier);
41474                name = self.expect_identifier_or_safe_keyword()?;
41475            }
41476
41477            let trailing_comments = self.previous_trailing_comments().to_vec();
41478            identifiers.push(Identifier {
41479                name,
41480                quoted: final_quoted,
41481                trailing_comments,
41482                span: None,
41483            });
41484
41485            if !self.match_token(TokenType::Comma) {
41486                break;
41487            }
41488        }
41489
41490        Ok(identifiers)
41491    }
41492
41493    /// Parse a comma-separated list of identifiers for index columns.
41494    /// Supports MySQL prefix lengths: col(16) and sort order: col DESC
41495    fn parse_index_identifier_list(&mut self) -> Result<Vec<Identifier>> {
41496        let mut identifiers = Vec::new();
41497
41498        loop {
41499            let quoted = self.check(TokenType::QuotedIdentifier);
41500            let name = self.expect_identifier_or_safe_keyword()?;
41501            let trailing_comments = self.previous_trailing_comments().to_vec();
41502
41503            // Check for prefix length: col(16)
41504            let mut display_name = name.clone();
41505            if self.match_token(TokenType::LParen) {
41506                if self.check(TokenType::Number) {
41507                    let len = self.advance().text;
41508                    display_name = format!("{}({})", name, len);
41509                }
41510                self.expect(TokenType::RParen)?;
41511            }
41512
41513            // Check for DESC/ASC sort order
41514            if self.match_token(TokenType::Desc) {
41515                display_name = format!("{} DESC", display_name);
41516            } else if self.match_token(TokenType::Asc) {
41517                display_name = format!("{} ASC", display_name);
41518            }
41519
41520            identifiers.push(Identifier {
41521                name: display_name,
41522                quoted,
41523                trailing_comments,
41524                span: None,
41525            });
41526
41527            if !self.match_token(TokenType::Comma) {
41528                break;
41529            }
41530        }
41531
41532        Ok(identifiers)
41533    }
41534    // =============================================================================
41535    // Auto-generated Missing Parser Methods
41536    // Total: 296 methods
41537    // =============================================================================
41538
41539    /// parse_add_column - Implemented from Python _parse_add_column
41540    /// Calls: parse_column, parse_column_def_with_exists
41541    #[allow(unused_variables, unused_mut)]
41542    pub fn parse_add_column(&mut self) -> Result<Option<Expression>> {
41543        if self.match_texts(&["FIRST", "AFTER"]) {
41544            // Matched one of: FIRST, AFTER
41545            return Ok(None);
41546        }
41547        Ok(None)
41548    }
41549
41550    /// parse_alias - Parses alias for an expression
41551    /// This method parses just the alias part (AS name or just name)
41552    /// Python: _parse_alias
41553    pub fn parse_alias(&mut self) -> Result<Option<Expression>> {
41554        // Check for AS keyword (explicit alias)
41555        let _explicit = self.match_token(TokenType::Alias);
41556
41557        // Parse the alias identifier
41558        if let Some(alias_expr) = self.parse_id_var()? {
41559            let alias_ident = match alias_expr {
41560                Expression::Identifier(id) => id,
41561                _ => return Ok(None),
41562            };
41563            // Return just the alias identifier wrapped in an expression
41564            return Ok(Some(Expression::Identifier(alias_ident)));
41565        }
41566
41567        Ok(None)
41568    }
41569
41570    /// parse_alias_with_expr - Wraps an expression with an alias if present
41571    pub fn parse_alias_with_expr(
41572        &mut self,
41573        this: Option<Expression>,
41574    ) -> Result<Option<Expression>> {
41575        if this.is_none() {
41576            return Ok(None);
41577        }
41578        let expr = this.unwrap();
41579
41580        // Check for AS keyword (explicit alias)
41581        // Accept both TokenType::Alias and TokenType::As
41582        let has_as = self.match_token(TokenType::Alias) || self.match_token(TokenType::As);
41583
41584        // Check for column aliases: (col1, col2)
41585        if has_as && self.match_token(TokenType::LParen) {
41586            let mut column_aliases = Vec::new();
41587            loop {
41588                if let Some(col_expr) = self.parse_id_var()? {
41589                    if let Expression::Identifier(id) = col_expr {
41590                        column_aliases.push(id);
41591                    }
41592                } else {
41593                    break;
41594                }
41595                if !self.match_token(TokenType::Comma) {
41596                    break;
41597                }
41598            }
41599            self.match_token(TokenType::RParen);
41600
41601            if !column_aliases.is_empty() {
41602                return Ok(Some(Expression::Alias(Box::new(Alias {
41603                    this: expr,
41604                    alias: Identifier::new(String::new()), // Empty alias when only column aliases
41605                    column_aliases,
41606                    pre_alias_comments: Vec::new(),
41607                    trailing_comments: Vec::new(),
41608                    inferred_type: None,
41609                }))));
41610            }
41611        }
41612
41613        // Parse the alias identifier
41614        if let Some(alias_expr) = self.parse_id_var()? {
41615            let alias_ident = match alias_expr {
41616                Expression::Identifier(id) => id,
41617                _ => return Ok(Some(expr)),
41618            };
41619            return Ok(Some(Expression::Alias(Box::new(Alias {
41620                this: expr,
41621                alias: alias_ident,
41622                column_aliases: Vec::new(),
41623                pre_alias_comments: Vec::new(),
41624                trailing_comments: Vec::new(),
41625                inferred_type: None,
41626            }))));
41627        }
41628
41629        Ok(Some(expr))
41630    }
41631
41632    /// parse_alter_diststyle - Implemented from Python _parse_alter_diststyle
41633    #[allow(unused_variables, unused_mut)]
41634    /// parse_alter_diststyle - Parses ALTER TABLE DISTSTYLE clause (Redshift)
41635    /// Python: parser.py:7797-7802
41636    pub fn parse_alter_diststyle(&mut self) -> Result<Option<Expression>> {
41637        // Check for ALL, EVEN, AUTO
41638        if self.match_texts(&["ALL", "EVEN", "AUTO"]) {
41639            let style = self.previous().text.to_ascii_uppercase();
41640            return Ok(Some(Expression::DistStyleProperty(Box::new(
41641                DistStyleProperty {
41642                    this: Box::new(Expression::Identifier(Identifier::new(style))),
41643                },
41644            ))));
41645        }
41646
41647        // KEY DISTKEY column
41648        if self.match_text_seq(&["KEY", "DISTKEY"]) {
41649            if let Some(column) = self.parse_column()? {
41650                return Ok(Some(Expression::DistStyleProperty(Box::new(
41651                    DistStyleProperty {
41652                        this: Box::new(column),
41653                    },
41654                ))));
41655            }
41656        }
41657
41658        Ok(None)
41659    }
41660
41661    /// parse_alter_session - Parses ALTER SESSION SET/UNSET statements
41662    /// Python: parser.py:7879-7889
41663    pub fn parse_alter_session(&mut self) -> Result<Option<Expression>> {
41664        // ALTER SESSION SET var = value, ...
41665        if self.match_token(TokenType::Set) {
41666            let mut expressions = Vec::new();
41667            loop {
41668                if let Some(item) = self.parse_set_item_assignment()? {
41669                    expressions.push(item);
41670                }
41671                if !self.match_token(TokenType::Comma) {
41672                    break;
41673                }
41674            }
41675            return Ok(Some(Expression::AlterSession(Box::new(AlterSession {
41676                expressions,
41677                unset: None,
41678            }))));
41679        }
41680
41681        // ALTER SESSION UNSET var, ...
41682        if self.match_text_seq(&["UNSET"]) {
41683            let mut expressions = Vec::new();
41684            loop {
41685                if let Some(var) = self.parse_id_var()? {
41686                    // For UNSET, we just use the identifier directly
41687                    expressions.push(var);
41688                }
41689                if !self.match_token(TokenType::Comma) {
41690                    break;
41691                }
41692            }
41693            return Ok(Some(Expression::AlterSession(Box::new(AlterSession {
41694                expressions,
41695                unset: Some(Box::new(Expression::Boolean(BooleanLiteral {
41696                    value: true,
41697                }))),
41698            }))));
41699        }
41700
41701        Ok(None)
41702    }
41703
41704    /// parse_alter_sortkey - Parses ALTER TABLE SORTKEY clause (Redshift)
41705    /// Python: parser.py:7804-7816
41706    pub fn parse_alter_sortkey(&mut self) -> Result<Option<Expression>> {
41707        self.parse_alter_sortkey_impl(None)
41708    }
41709
41710    /// Implementation of parse_alter_sortkey with compound option
41711    pub fn parse_alter_sortkey_impl(
41712        &mut self,
41713        compound: Option<bool>,
41714    ) -> Result<Option<Expression>> {
41715        // For compound sortkey, match SORTKEY keyword
41716        if compound == Some(true) {
41717            self.match_text_seq(&["SORTKEY"]);
41718        }
41719
41720        // Check for (column_list) syntax
41721        if self.check(TokenType::LParen) {
41722            let wrapped = self.parse_wrapped_id_vars()?;
41723            // Extract expressions from Tuple
41724            let expressions = if let Some(Expression::Tuple(t)) = wrapped {
41725                t.expressions
41726            } else {
41727                Vec::new()
41728            };
41729            return Ok(Some(Expression::AlterSortKey(Box::new(AlterSortKey {
41730                this: None,
41731                expressions,
41732                compound: compound
41733                    .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
41734            }))));
41735        }
41736
41737        // Check for AUTO or NONE
41738        if self.match_texts(&["AUTO", "NONE"]) {
41739            let style = self.previous().text.to_ascii_uppercase();
41740            return Ok(Some(Expression::AlterSortKey(Box::new(AlterSortKey {
41741                this: Some(Box::new(Expression::Identifier(Identifier::new(style)))),
41742                expressions: Vec::new(),
41743                compound: compound
41744                    .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
41745            }))));
41746        }
41747
41748        Ok(None)
41749    }
41750
41751    /// parse_alter_table_add - Parses ALTER TABLE ADD clause
41752    /// Python: parser.py:7715-7751
41753    pub fn parse_alter_table_add(&mut self) -> Result<Option<Expression>> {
41754        // Check for ADD keyword (optional in some contexts)
41755        self.match_text_seq(&["ADD"]);
41756
41757        // Check for INDEX/KEY with optional FULLTEXT/SPATIAL prefix (MySQL)
41758        // Syntax: ADD [FULLTEXT|SPATIAL] {INDEX|KEY} [name] (columns) [USING {BTREE|HASH}]
41759        let kind = if self.match_identifier("FULLTEXT") {
41760            Some("FULLTEXT".to_string())
41761        } else if self.match_identifier("SPATIAL") {
41762            Some("SPATIAL".to_string())
41763        } else {
41764            None
41765        };
41766
41767        if self.check(TokenType::Index) || self.check(TokenType::Key) || kind.is_some() {
41768            // Consume INDEX or KEY keyword, track which was used
41769            let use_key_keyword = if self.match_token(TokenType::Key) {
41770                true
41771            } else {
41772                self.match_token(TokenType::Index);
41773                false
41774            };
41775
41776            // Optional index name (before the columns)
41777            let name = if !self.check(TokenType::LParen) && !self.check(TokenType::Using) {
41778                Some(self.expect_identifier_with_quoted()?)
41779            } else {
41780                None
41781            };
41782
41783            // Parse columns (with optional prefix length and DESC)
41784            self.expect(TokenType::LParen)?;
41785            let columns = self.parse_index_identifier_list()?;
41786            self.expect(TokenType::RParen)?;
41787
41788            // Parse optional USING BTREE|HASH
41789            let modifiers = self.parse_constraint_modifiers();
41790
41791            return Ok(Some(Expression::AlterTable(Box::new(AlterTable {
41792                name: TableRef::new(""),
41793                actions: vec![AlterTableAction::AddConstraint(TableConstraint::Index {
41794                    name,
41795                    columns,
41796                    kind,
41797                    modifiers,
41798                    use_key_keyword,
41799                    expression: None,
41800                    index_type: None,
41801                    granularity: None,
41802                })],
41803                if_exists: false,
41804                algorithm: None,
41805                lock: None,
41806                with_check: None,
41807                partition: None,
41808                on_cluster: None,
41809                table_modifier: None,
41810            }))));
41811        }
41812
41813        // Check for constraint keywords (PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, CONSTRAINT)
41814        if self.check(TokenType::PrimaryKey)
41815            || self.check(TokenType::ForeignKey)
41816            || self.check(TokenType::Unique)
41817            || self.check(TokenType::Check)
41818            || self.check(TokenType::Constraint)
41819        {
41820            // Parse a single constraint and return it wrapped in Constraint
41821            if let Some(constraint) = self.parse_constraint()? {
41822                return Ok(Some(Expression::Constraint(Box::new(Constraint {
41823                    this: Box::new(constraint),
41824                    expressions: Vec::new(),
41825                }))));
41826            }
41827        }
41828
41829        // Check for COLUMNS keyword (batch column addition)
41830        if self.match_text_seq(&["COLUMNS"]) {
41831            // Parse schema or column definitions
41832            if let Some(schema) = self.parse_schema()? {
41833                return Ok(Some(schema));
41834            }
41835        }
41836
41837        // Check for IF NOT EXISTS PARTITION (must check before parse_add_column)
41838        let exists = self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
41839        if self.match_token(TokenType::Partition) {
41840            // Parse PARTITION(key = value, ...)
41841            self.expect(TokenType::LParen)?;
41842            let mut partition_exprs = Vec::new();
41843            loop {
41844                if let Some(expr) = self.parse_conjunction()? {
41845                    partition_exprs.push(expr);
41846                }
41847                if !self.match_token(TokenType::Comma) {
41848                    break;
41849                }
41850            }
41851            self.expect(TokenType::RParen)?;
41852
41853            let partition = Expression::Partition(Box::new(crate::expressions::Partition {
41854                expressions: partition_exprs,
41855                subpartition: false,
41856            }));
41857
41858            let location = if self.match_text_seq(&["LOCATION"]) {
41859                self.parse_property()?
41860            } else {
41861                None
41862            };
41863            return Ok(Some(Expression::AddPartition(Box::new(AddPartition {
41864                this: Box::new(partition),
41865                exists,
41866                location: location.map(Box::new),
41867            }))));
41868        }
41869
41870        // Try to parse column definition (after checking for PARTITION)
41871        if let Some(column) = self.parse_add_column()? {
41872            return Ok(Some(column));
41873        }
41874
41875        Ok(None)
41876    }
41877
41878    /// parse_alter_table_alter - Parses ALTER TABLE ALTER COLUMN clause
41879    /// Python: parser.py:7753-7795
41880    pub fn parse_alter_table_alter(&mut self) -> Result<Option<Expression>> {
41881        // Match optional COLUMN keyword
41882        self.match_token(TokenType::Column);
41883
41884        // Parse the column name - required for ALTER COLUMN
41885        let column = match self.parse_field()? {
41886            Some(c) => c,
41887            None => return Ok(None),
41888        };
41889
41890        // DROP DEFAULT
41891        if self.match_keywords(&[TokenType::Drop, TokenType::Default]) {
41892            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41893                this: Box::new(column),
41894                dtype: None,
41895                collate: None,
41896                using: None,
41897                default: None,
41898                drop: Some(Box::new(Expression::Boolean(BooleanLiteral {
41899                    value: true,
41900                }))),
41901                allow_null: None,
41902                comment: None,
41903                visible: None,
41904                rename_to: None,
41905            }))));
41906        }
41907
41908        // SET DEFAULT expr
41909        if self.match_keywords(&[TokenType::Set, TokenType::Default]) {
41910            let default_val = self.parse_disjunction()?;
41911            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41912                this: Box::new(column),
41913                dtype: None,
41914                collate: None,
41915                using: None,
41916                default: default_val.map(Box::new),
41917                drop: None,
41918                allow_null: None,
41919                comment: None,
41920                visible: None,
41921                rename_to: None,
41922            }))));
41923        }
41924
41925        // COMMENT 'string'
41926        if self.match_token(TokenType::Comment) {
41927            let comment_val = self.parse_string()?;
41928            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41929                this: Box::new(column),
41930                dtype: None,
41931                collate: None,
41932                using: None,
41933                default: None,
41934                drop: None,
41935                allow_null: None,
41936                comment: comment_val.map(Box::new),
41937                visible: None,
41938                rename_to: None,
41939            }))));
41940        }
41941
41942        // DROP NOT NULL
41943        if self.match_text_seq(&["DROP", "NOT", "NULL"]) {
41944            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41945                this: Box::new(column),
41946                dtype: None,
41947                collate: None,
41948                using: None,
41949                default: None,
41950                drop: Some(Box::new(Expression::Boolean(BooleanLiteral {
41951                    value: true,
41952                }))),
41953                allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
41954                    value: true,
41955                }))),
41956                comment: None,
41957                visible: None,
41958                rename_to: None,
41959            }))));
41960        }
41961
41962        // SET NOT NULL
41963        if self.match_text_seq(&["SET", "NOT", "NULL"]) {
41964            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41965                this: Box::new(column),
41966                dtype: None,
41967                collate: None,
41968                using: None,
41969                default: None,
41970                drop: None,
41971                allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
41972                    value: false,
41973                }))),
41974                comment: None,
41975                visible: None,
41976                rename_to: None,
41977            }))));
41978        }
41979
41980        // SET VISIBLE
41981        if self.match_text_seq(&["SET", "VISIBLE"]) {
41982            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41983                this: Box::new(column),
41984                dtype: None,
41985                collate: None,
41986                using: None,
41987                default: None,
41988                drop: None,
41989                allow_null: None,
41990                comment: None,
41991                visible: Some(Box::new(Expression::Identifier(Identifier::new(
41992                    "VISIBLE".to_string(),
41993                )))),
41994                rename_to: None,
41995            }))));
41996        }
41997
41998        // SET INVISIBLE
41999        if self.match_text_seq(&["SET", "INVISIBLE"]) {
42000            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
42001                this: Box::new(column),
42002                dtype: None,
42003                collate: None,
42004                using: None,
42005                default: None,
42006                drop: None,
42007                allow_null: None,
42008                comment: None,
42009                visible: Some(Box::new(Expression::Identifier(Identifier::new(
42010                    "INVISIBLE".to_string(),
42011                )))),
42012                rename_to: None,
42013            }))));
42014        }
42015
42016        // [SET DATA] TYPE type [COLLATE collation] [USING expr]
42017        self.match_text_seq(&["SET", "DATA"]);
42018        self.match_text_seq(&["TYPE"]);
42019
42020        let dtype = self.parse_types()?;
42021        let collate = if self.match_token(TokenType::Collate) {
42022            self.parse_term()?
42023        } else {
42024            None
42025        };
42026        let using = if self.match_token(TokenType::Using) {
42027            self.parse_disjunction()?
42028        } else {
42029            None
42030        };
42031
42032        Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
42033            this: Box::new(column),
42034            dtype: dtype.map(Box::new),
42035            collate: collate.map(Box::new),
42036            using: using.map(Box::new),
42037            default: None,
42038            drop: None,
42039            allow_null: None,
42040            comment: None,
42041            visible: None,
42042            rename_to: None,
42043        }))))
42044    }
42045
42046    /// Parse ALTER TABLE DROP action
42047    /// Note: Main ALTER TABLE DROP logic is implemented inline in parse_alter_table
42048    /// This method provides a separate entry point for the same functionality
42049    pub fn parse_alter_table_drop(&mut self) -> Result<Option<Expression>> {
42050        // Check for IF EXISTS before PARTITION
42051        let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
42052
42053        // Check if this is DROP PARTITION
42054        if self.check(TokenType::Partition) {
42055            return self.parse_drop_partition_with_exists(exists);
42056        }
42057
42058        // Check for DROP FOREIGN KEY (Oracle/MySQL)
42059        if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
42060            let name = self.expect_identifier_with_quoted()?;
42061            return Ok(Some(Expression::AlterTable(Box::new(AlterTable {
42062                name: TableRef::new(""),
42063                actions: vec![AlterTableAction::DropForeignKey { name }],
42064                if_exists: false,
42065                algorithm: None,
42066                lock: None,
42067                with_check: None,
42068                partition: None,
42069                on_cluster: None,
42070                table_modifier: None,
42071            }))));
42072        }
42073
42074        // Check for DROP COLUMNS (col1, col2, ...) syntax (Spark/Databricks)
42075        if self.check_identifier("COLUMNS") && self.check_next(TokenType::LParen) {
42076            self.skip(); // consume COLUMNS
42077            self.expect(TokenType::LParen)?;
42078            let mut columns = Vec::new();
42079            loop {
42080                if let Some(col) = self.parse_identifier()? {
42081                    columns.push(col);
42082                }
42083                if !self.match_token(TokenType::Comma) {
42084                    break;
42085                }
42086            }
42087            self.expect(TokenType::RParen)?;
42088            if columns.is_empty() {
42089                return Ok(None);
42090            } else if columns.len() == 1 {
42091                return Ok(Some(columns.remove(0)));
42092            } else {
42093                return Ok(Some(Expression::Tuple(Box::new(Tuple {
42094                    expressions: columns,
42095                }))));
42096            }
42097        }
42098
42099        // Otherwise, parse as DROP COLUMN(s)
42100        let mut columns = Vec::new();
42101
42102        // Parse first column
42103        if let Some(col) = self.parse_drop_column()? {
42104            columns.push(col);
42105        }
42106
42107        // Parse additional columns (comma-separated)
42108        while self.match_token(TokenType::Comma) {
42109            // Match optional DROP keyword before next column
42110            self.match_token(TokenType::Drop);
42111            if let Some(col) = self.parse_drop_column()? {
42112                columns.push(col);
42113            }
42114        }
42115
42116        if columns.is_empty() {
42117            Ok(None)
42118        } else if columns.len() == 1 {
42119            Ok(Some(columns.remove(0)))
42120        } else {
42121            // Multiple columns - wrap in a Tuple
42122            Ok(Some(Expression::Tuple(Box::new(Tuple {
42123                expressions: columns,
42124            }))))
42125        }
42126    }
42127
42128    /// parse_alter_table_rename - Parses ALTER TABLE RENAME clause
42129    /// Python: parser.py:7828-7841
42130    pub fn parse_alter_table_rename(&mut self) -> Result<Option<Expression>> {
42131        // RENAME COLUMN old_name TO new_name
42132        if self.match_token(TokenType::Column) {
42133            let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
42134            let old_column = match self.parse_column()? {
42135                Some(c) => c,
42136                None => return Ok(None),
42137            };
42138
42139            if !self.match_text_seq(&["TO"]) {
42140                return Ok(None);
42141            }
42142
42143            let new_column = self.parse_column()?;
42144
42145            return Ok(Some(Expression::RenameColumn(Box::new(RenameColumn {
42146                this: Box::new(old_column),
42147                to: new_column.map(Box::new),
42148                exists,
42149            }))));
42150        }
42151
42152        // RENAME TO new_table_name
42153        if self.match_text_seq(&["TO"]) {
42154            // Return the table expression directly - the caller will handle it as a rename target
42155            let new_table = self.parse_table()?;
42156            return Ok(new_table);
42157        }
42158
42159        // SQLite allows: RENAME old_name TO new_name (without COLUMN keyword)
42160        // Try to parse as column rename if followed by identifier and TO
42161        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
42162            let old_column = match self.parse_column()? {
42163                Some(c) => c,
42164                None => return Ok(None),
42165            };
42166
42167            if self.match_text_seq(&["TO"]) {
42168                let new_column = self.parse_column()?;
42169                return Ok(Some(Expression::RenameColumn(Box::new(RenameColumn {
42170                    this: Box::new(old_column),
42171                    to: new_column.map(Box::new),
42172                    exists: false,
42173                }))));
42174            } else {
42175                // Not TO after identifier - put it back and return error
42176                return Err(self.parse_error("Expected COLUMN or TO after RENAME"));
42177            }
42178        }
42179
42180        Ok(None)
42181    }
42182
42183    /// parse_alter_table_set - Parses ALTER TABLE SET clause
42184    /// Python: parser.py:7843-7877
42185    pub fn parse_alter_table_set(&mut self) -> Result<Option<Expression>> {
42186        let mut alter_set = AlterSet {
42187            expressions: Vec::new(),
42188            option: None,
42189            tablespace: None,
42190            access_method: None,
42191            file_format: None,
42192            copy_options: None,
42193            tag: None,
42194            location: None,
42195            serde: None,
42196        };
42197
42198        // SET AUTHORIZATION [ROLE] user
42199        if self.match_token(TokenType::Authorization) {
42200            let mut auth_text = "AUTHORIZATION ".to_string();
42201            if self.match_texts(&["ROLE"]) {
42202                auth_text.push_str("ROLE ");
42203            }
42204            let user = self.expect_identifier()?;
42205            auth_text.push_str(&user);
42206            alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(auth_text))));
42207            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42208        }
42209
42210        // SET PROPERTIES prop = value, ...
42211        if self.match_text_seq(&["PROPERTIES"]) {
42212            let mut assignments = Vec::new();
42213            loop {
42214                // Parse property name (could be identifier or string literal)
42215                let key = if self.check(TokenType::String) {
42216                    self.parse_string()?.unwrap_or(Expression::Null(Null))
42217                } else {
42218                    let name = self.expect_identifier()?;
42219                    Expression::Identifier(Identifier::new(name))
42220                };
42221                self.expect(TokenType::Eq)?;
42222                // Parse value (could be DEFAULT or an expression)
42223                let value = if self.match_token(TokenType::Default) {
42224                    Expression::Identifier(Identifier::new("DEFAULT".to_string()))
42225                } else {
42226                    self.parse_expression()?
42227                };
42228                assignments.push(Expression::Eq(Box::new(BinaryOp {
42229                    left: key,
42230                    right: value,
42231                    left_comments: Vec::new(),
42232                    operator_comments: Vec::new(),
42233                    trailing_comments: Vec::new(),
42234                    inferred_type: None,
42235                })));
42236                if !self.match_token(TokenType::Comma) {
42237                    break;
42238                }
42239            }
42240            alter_set.expressions = assignments;
42241            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42242        }
42243
42244        // SET (properties) or SET TABLE PROPERTIES (properties)
42245        if self.check(TokenType::LParen) || self.match_text_seq(&["TABLE", "PROPERTIES"]) {
42246            let assignments = self.parse_wrapped_csv_assignments()?;
42247            alter_set.expressions = assignments;
42248            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42249        }
42250
42251        // SET FILESTREAM_ON = value
42252        if self.match_text_seq(&["FILESTREAM_ON"]) {
42253            if let Some(assignment) = self.parse_assignment()? {
42254                alter_set.expressions = vec![assignment];
42255            }
42256            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42257        }
42258
42259        // SET LOGGED or SET UNLOGGED
42260        if self.match_texts(&["LOGGED", "UNLOGGED"]) {
42261            let option = self.previous().text.to_ascii_uppercase();
42262            alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(option))));
42263            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42264        }
42265
42266        // SET WITHOUT CLUSTER or SET WITHOUT OIDS
42267        if self.match_text_seq(&["WITHOUT"]) {
42268            if self.match_texts(&["CLUSTER", "OIDS"]) {
42269                let option = format!("WITHOUT {}", self.previous().text.to_ascii_uppercase());
42270                alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(option))));
42271                return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42272            }
42273        }
42274
42275        // SET LOCATION path
42276        if self.match_text_seq(&["LOCATION"]) {
42277            let loc = self.parse_field()?;
42278            alter_set.location = loc.map(Box::new);
42279            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42280        }
42281
42282        // SET ACCESS METHOD method
42283        if self.match_text_seq(&["ACCESS", "METHOD"]) {
42284            let method = self.parse_field()?;
42285            alter_set.access_method = method.map(Box::new);
42286            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42287        }
42288
42289        // SET TABLESPACE name
42290        if self.match_text_seq(&["TABLESPACE"]) {
42291            let tablespace = self.parse_field()?;
42292            alter_set.tablespace = tablespace.map(Box::new);
42293            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42294        }
42295
42296        // SET FILE FORMAT format or SET FILEFORMAT format
42297        if self.match_text_seq(&["FILE", "FORMAT"]) || self.match_text_seq(&["FILEFORMAT"]) {
42298            let format = self.parse_field()?;
42299            alter_set.file_format = format.map(Box::new);
42300            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42301        }
42302
42303        // SET STAGE_FILE_FORMAT = (options)
42304        if self.match_text_seq(&["STAGE_FILE_FORMAT"]) {
42305            let options = self.parse_wrapped_options()?;
42306            alter_set.file_format = options.map(Box::new);
42307            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42308        }
42309
42310        // SET STAGE_COPY_OPTIONS = (options)
42311        if self.match_text_seq(&["STAGE_COPY_OPTIONS"]) {
42312            let options = self.parse_wrapped_options()?;
42313            alter_set.copy_options = options.map(Box::new);
42314            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42315        }
42316
42317        // SET TAG or SET TAGS
42318        if self.match_text_seq(&["TAG"]) || self.match_text_seq(&["TAGS"]) {
42319            let mut tags = Vec::new();
42320            loop {
42321                if let Some(assignment) = self.parse_assignment()? {
42322                    tags.push(assignment);
42323                }
42324                if !self.match_token(TokenType::Comma) {
42325                    break;
42326                }
42327            }
42328            if !tags.is_empty() {
42329                alter_set.tag = Some(Box::new(Expression::Tuple(Box::new(Tuple {
42330                    expressions: tags,
42331                }))));
42332            }
42333            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42334        }
42335
42336        // SET SERDE 'class' [WITH SERDEPROPERTIES (...)]
42337        if self.match_text_seq(&["SERDE"]) {
42338            let serde = self.parse_field()?;
42339            alter_set.serde = serde.map(Box::new);
42340
42341            // Parse optional properties
42342            let properties = self.parse_wrapped()?;
42343            if let Some(props) = properties {
42344                alter_set.expressions = vec![props];
42345            }
42346            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42347        }
42348
42349        Ok(None)
42350    }
42351
42352    /// Helper to parse wrapped CSV of assignments
42353    fn parse_wrapped_csv_assignments(&mut self) -> Result<Vec<Expression>> {
42354        if !self.match_token(TokenType::LParen) {
42355            return Ok(Vec::new());
42356        }
42357        let mut assignments = Vec::new();
42358        loop {
42359            if let Some(assignment) = self.parse_assignment()? {
42360                assignments.push(assignment);
42361            }
42362            if !self.match_token(TokenType::Comma) {
42363                break;
42364            }
42365        }
42366        self.expect(TokenType::RParen)?;
42367        Ok(assignments)
42368    }
42369
42370    /// parse_analyze - Implemented from Python _parse_analyze
42371    /// Calls: parse_table_parts, parse_number, parse_table
42372    #[allow(unused_variables, unused_mut)]
42373    /// parse_analyze - Parses ANALYZE statement
42374    /// Python: parser.py:7937-7999
42375    pub fn parse_analyze(&mut self) -> Result<Option<Expression>> {
42376        // If no more tokens, return empty Analyze
42377        if self.is_at_end() {
42378            return Ok(Some(Expression::Analyze(Box::new(Analyze {
42379                kind: None,
42380                this: None,
42381                options: Vec::new(),
42382                mode: None,
42383                partition: None,
42384                expression: None,
42385                properties: Vec::new(),
42386                columns: Vec::new(),
42387            }))));
42388        }
42389
42390        // Parse options (VERBOSE, SKIP_LOCKED, etc.)
42391        // StarRocks uses FULL and SAMPLE as options
42392        let mut options = Vec::new();
42393        let analyze_styles = [
42394            "VERBOSE",
42395            "SKIP_LOCKED",
42396            "BUFFER_USAGE_LIMIT",
42397            "FULL",
42398            "SAMPLE",
42399        ];
42400        while self.match_texts(&analyze_styles) {
42401            let style = self.previous().text.to_ascii_uppercase();
42402            if style == "BUFFER_USAGE_LIMIT" {
42403                // Parse number after BUFFER_USAGE_LIMIT
42404                if let Some(num) = self.parse_number()? {
42405                    options.push(Expression::Identifier(Identifier::new(format!(
42406                        "BUFFER_USAGE_LIMIT {}",
42407                        if let Expression::Literal(lit) = &num {
42408                            if let Literal::Number(n) = lit.as_ref() {
42409                                n.clone()
42410                            } else {
42411                                String::new()
42412                            }
42413                        } else {
42414                            String::new()
42415                        }
42416                    ))));
42417                }
42418            } else {
42419                options.push(Expression::Identifier(Identifier::new(style)));
42420            }
42421        }
42422
42423        let mut this: Option<Expression> = None;
42424        let mut kind: Option<String> = None;
42425        let mut inner_expression: Option<Expression> = None;
42426
42427        // Parse TABLE or INDEX
42428        if self.match_token(TokenType::Table) {
42429            kind = Some("TABLE".to_string());
42430            this = self.parse_table_parts()?;
42431        } else if self.match_token(TokenType::Index) {
42432            kind = Some("INDEX".to_string());
42433            this = self.parse_table_parts()?;
42434        } else if self.match_text_seq(&["TABLES"]) {
42435            kind = Some("TABLES".to_string());
42436            if self.match_token(TokenType::From) || self.match_token(TokenType::In) {
42437                let dir = self.previous().text.to_ascii_uppercase();
42438                kind = Some(format!("TABLES {}", dir));
42439                // Parse database name as identifier
42440                let db_name = self.expect_identifier()?;
42441                this = Some(Expression::Identifier(Identifier::new(db_name)));
42442            }
42443        } else if self.match_text_seq(&["DATABASE"]) {
42444            kind = Some("DATABASE".to_string());
42445            this = self.parse_table_parts()?;
42446        } else if self.match_text_seq(&["CLUSTER"]) {
42447            kind = Some("CLUSTER".to_string());
42448            this = self.parse_table_parts()?;
42449        } else if self.match_texts(&["LOCAL", "NO_WRITE_TO_BINLOG"]) {
42450            // MySQL: ANALYZE LOCAL TABLE tbl / ANALYZE NO_WRITE_TO_BINLOG TABLE tbl
42451            let opt_text = self.previous().text.to_ascii_uppercase();
42452            options.push(Expression::Identifier(Identifier::new(opt_text)));
42453            if self.match_token(TokenType::Table) {
42454                kind = Some("TABLE".to_string());
42455            }
42456            this = self.parse_table_parts()?;
42457        } else if self.match_text_seq(&["COMPUTE"]) {
42458            // Check ANALYZE_EXPRESSION_PARSERS keywords before fallback to parse_table_parts
42459            // Python: elif self._match_texts(self.ANALYZE_EXPRESSION_PARSERS)
42460            inner_expression = self.parse_analyze_statistics()?;
42461        } else if self.match_text_seq(&["DELETE"]) {
42462            inner_expression = self.parse_analyze_delete()?;
42463        } else if self.match_text_seq(&["VALIDATE"]) {
42464            inner_expression = self.parse_analyze_validate()?;
42465        } else if self.match_text_seq(&["LIST"]) {
42466            inner_expression = self.parse_analyze_list()?;
42467        } else if self.match_text_seq(&["DROP"]) {
42468            inner_expression = self.parse_analyze_histogram()?;
42469        } else if self.match_text_seq(&["UPDATE"]) {
42470            inner_expression = self.parse_analyze_histogram()?;
42471        } else if self.match_texts(&["ALL", "PREDICATE"]) {
42472            inner_expression = self.parse_analyze_columns()?;
42473        } else {
42474            // Try to parse table directly (empty kind - https://prestodb.io/docs/current/sql/analyze.html)
42475            this = self.parse_table_parts()?;
42476        }
42477
42478        // Parse optional column list: ANALYZE tbl(col1, col2) (PostgreSQL)
42479        let columns = if this.is_some() && self.match_token(TokenType::LParen) {
42480            let mut cols = Vec::new();
42481            loop {
42482                cols.push(self.expect_identifier_or_keyword()?);
42483                if !self.match_token(TokenType::Comma) {
42484                    break;
42485                }
42486            }
42487            self.expect(TokenType::RParen)?;
42488            cols
42489        } else {
42490            Vec::new()
42491        };
42492
42493        // Parse optional PARTITION
42494        let partition = self.parse_partition()?;
42495
42496        // Parse optional WITH SYNC/ASYNC MODE or WITH (prop=val, ...) for Presto
42497        let mut mode = None;
42498        let mut properties = Vec::new();
42499
42500        if self.match_text_seq(&["WITH", "SYNC", "MODE"]) {
42501            mode = Some(Box::new(Expression::Identifier(Identifier::new(
42502                "WITH SYNC MODE".to_string(),
42503            ))));
42504        } else if self.match_text_seq(&["WITH", "ASYNC", "MODE"]) {
42505            mode = Some(Box::new(Expression::Identifier(Identifier::new(
42506                "WITH ASYNC MODE".to_string(),
42507            ))));
42508        } else if self.match_text_seq(&["WITH"]) {
42509            // Presto syntax: ANALYZE tbl WITH (prop1=val1, prop2=val2)
42510            if self.match_token(TokenType::LParen) {
42511                loop {
42512                    // Parse key=value pairs
42513                    let key = self.parse_id_var()?;
42514                    if key.is_none() {
42515                        break;
42516                    }
42517
42518                    // Expect = sign
42519                    if self.match_token(TokenType::Eq) {
42520                        // Parse the value
42521                        let value = self.parse_primary()?;
42522                        if let Some(k) = key {
42523                            properties.push(Expression::Property(Box::new(Property {
42524                                this: Box::new(k),
42525                                value: Some(Box::new(value)),
42526                            })));
42527                        }
42528                    } else if let Some(k) = key {
42529                        // Key without value
42530                        properties.push(Expression::Property(Box::new(Property {
42531                            this: Box::new(k),
42532                            value: None,
42533                        })));
42534                    }
42535
42536                    if !self.match_token(TokenType::Comma) {
42537                        break;
42538                    }
42539                }
42540                self.expect(TokenType::RParen)?;
42541            }
42542        }
42543
42544        // Parse optional inner expressions (COMPUTE, DELETE, etc.)
42545        // Only if inner_expression wasn't already set (for cases like ANALYZE TABLE tbl VALIDATE...)
42546        if inner_expression.is_none() {
42547            if self.match_text_seq(&["COMPUTE"]) {
42548                inner_expression = self.parse_analyze_statistics()?;
42549            } else if self.match_text_seq(&["DELETE"]) {
42550                inner_expression = self.parse_analyze_delete()?;
42551            } else if self.match_text_seq(&["VALIDATE"]) {
42552                inner_expression = self.parse_analyze_validate()?;
42553            } else if self.match_text_seq(&["LIST"]) {
42554                inner_expression = self.parse_analyze_list()?;
42555            } else if self.match_text_seq(&["DROP"]) {
42556                inner_expression = self.parse_analyze_histogram()?;
42557            } else if self.match_text_seq(&["UPDATE"]) {
42558                inner_expression = self.parse_analyze_histogram()?;
42559            } else if self.match_texts(&["ALL", "PREDICATE"]) {
42560                // Redshift: ANALYZE TBL ALL COLUMNS / ANALYZE TBL PREDICATE COLUMNS
42561                inner_expression = self.parse_analyze_columns()?;
42562            }
42563        }
42564
42565        // Parse optional properties (if not already parsed from WITH clause)
42566        // StarRocks syntax: ANALYZE TABLE TBL PROPERTIES ('prop1'=val1, 'prop2'=val2)
42567        if properties.is_empty() && self.match_text_seq(&["PROPERTIES"]) {
42568            if self.match_token(TokenType::LParen) {
42569                loop {
42570                    // Parse key (can be a string literal or identifier)
42571                    let key = if self.check(TokenType::String) {
42572                        self.skip();
42573                        let key_str = self.previous().text.clone();
42574                        Expression::Literal(Box::new(Literal::String(key_str)))
42575                    } else {
42576                        self.parse_id_var()?
42577                            .unwrap_or(Expression::Identifier(Identifier::new(String::new())))
42578                    };
42579
42580                    // Expect = sign
42581                    if self.match_token(TokenType::Eq) {
42582                        // Parse the value
42583                        let value = self.parse_primary()?;
42584                        properties.push(Expression::Property(Box::new(Property {
42585                            this: Box::new(key),
42586                            value: Some(Box::new(value)),
42587                        })));
42588                    } else {
42589                        // Key without value
42590                        properties.push(Expression::Property(Box::new(Property {
42591                            this: Box::new(key),
42592                            value: None,
42593                        })));
42594                    }
42595
42596                    if !self.match_token(TokenType::Comma) {
42597                        break;
42598                    }
42599                }
42600                self.expect(TokenType::RParen)?;
42601            }
42602        }
42603
42604        Ok(Some(Expression::Analyze(Box::new(Analyze {
42605            kind,
42606            this: this.map(Box::new),
42607            options,
42608            mode,
42609            partition: partition.map(Box::new),
42610            expression: inner_expression.map(Box::new),
42611            properties,
42612            columns,
42613        }))))
42614    }
42615
42616    /// parse_analyze_columns - Parses ANALYZE ... COLUMNS
42617    /// Python: parser.py:8055-8059
42618    /// Note: AnalyzeColumns not in expressions.rs, using Identifier instead
42619    pub fn parse_analyze_columns(&mut self) -> Result<Option<Expression>> {
42620        let prev_text = self.previous().text.to_ascii_uppercase();
42621        if self.match_text_seq(&["COLUMNS"]) {
42622            return Ok(Some(Expression::Identifier(Identifier::new(format!(
42623                "{} COLUMNS",
42624                prev_text
42625            )))));
42626        }
42627        Ok(None)
42628    }
42629
42630    /// parse_analyze_delete - Parses ANALYZE DELETE STATISTICS
42631    /// Python: parser.py:8061-8065
42632    pub fn parse_analyze_delete(&mut self) -> Result<Option<Expression>> {
42633        let kind = if self.match_text_seq(&["SYSTEM"]) {
42634            Some("SYSTEM".to_string())
42635        } else {
42636            None
42637        };
42638
42639        if self.match_text_seq(&["STATISTICS"]) {
42640            return Ok(Some(Expression::AnalyzeDelete(Box::new(AnalyzeDelete {
42641                kind,
42642            }))));
42643        }
42644
42645        Ok(None)
42646    }
42647
42648    /// parse_analyze_histogram - Parses ANALYZE ... HISTOGRAM ON
42649    /// Python: parser.py:8073-8108
42650    pub fn parse_analyze_histogram(&mut self) -> Result<Option<Expression>> {
42651        let action = self.previous().text.to_ascii_uppercase(); // DROP or UPDATE
42652        let mut expressions = Vec::new();
42653        let mut update_options: Option<Box<Expression>> = None;
42654        let mut expression: Option<Box<Expression>> = None;
42655
42656        if !self.match_text_seq(&["HISTOGRAM", "ON"]) {
42657            return Ok(None);
42658        }
42659
42660        // Parse column references
42661        loop {
42662            if let Some(col) = self.parse_column_reference()? {
42663                expressions.push(col);
42664            } else {
42665                break;
42666            }
42667            if !self.match_token(TokenType::Comma) {
42668                break;
42669            }
42670        }
42671
42672        // Parse USING DATA 'json_data' (MySQL) - must check before WITH
42673        if self.match_text_seq(&["USING", "DATA"]) {
42674            if self.check(TokenType::String) {
42675                let tok = self.advance();
42676                expression = Some(Box::new(Expression::Identifier(Identifier::new(format!(
42677                    "USING DATA '{}'",
42678                    tok.text
42679                )))));
42680            } else {
42681                expression = Some(Box::new(Expression::Identifier(Identifier::new(
42682                    "USING DATA".to_string(),
42683                ))));
42684            }
42685        }
42686
42687        // Parse WITH options - can have two WITH clauses:
42688        // 1. WITH SYNC/ASYNC MODE (optional)
42689        // 2. WITH n BUCKETS (optional)
42690        // StarRocks syntax: WITH SYNC MODE WITH 5 BUCKETS
42691        let mut mode_str: Option<String> = None;
42692        let mut buckets_str: Option<String> = None;
42693
42694        if self.match_token(TokenType::With) {
42695            if self.match_texts(&["SYNC", "ASYNC"]) {
42696                let mode = self.previous().text.to_ascii_uppercase();
42697                if self.match_text_seq(&["MODE"]) {
42698                    mode_str = Some(format!("WITH {} MODE", mode));
42699                }
42700                // Check for second WITH clause for buckets
42701                if self.match_token(TokenType::With) {
42702                    if let Some(num) = self.parse_number()? {
42703                        if self.match_text_seq(&["BUCKETS"]) {
42704                            let num_str = if let Expression::Literal(lit) = &num {
42705                                if let Literal::Number(n) = lit.as_ref() {
42706                                    n.clone()
42707                                } else {
42708                                    String::new()
42709                                }
42710                            } else {
42711                                String::new()
42712                            };
42713                            buckets_str = Some(format!("WITH {} BUCKETS", num_str));
42714                        }
42715                    }
42716                }
42717            } else if let Some(num) = self.parse_number()? {
42718                if self.match_text_seq(&["BUCKETS"]) {
42719                    let num_str = if let Expression::Literal(lit) = &num {
42720                        if let Literal::Number(n) = lit.as_ref() {
42721                            n.clone()
42722                        } else {
42723                            String::new()
42724                        }
42725                    } else {
42726                        String::new()
42727                    };
42728                    buckets_str = Some(format!("WITH {} BUCKETS", num_str));
42729                }
42730            }
42731        }
42732
42733        // Combine mode and buckets into expression
42734        match (mode_str, buckets_str) {
42735            (Some(m), Some(b)) => {
42736                expression = Some(Box::new(Expression::Identifier(Identifier::new(format!(
42737                    "{} {}",
42738                    m, b
42739                )))));
42740            }
42741            (Some(m), None) => {
42742                expression = Some(Box::new(Expression::Identifier(Identifier::new(m))));
42743            }
42744            (None, Some(b)) => {
42745                expression = Some(Box::new(Expression::Identifier(Identifier::new(b))));
42746            }
42747            (None, None) => {}
42748        }
42749
42750        // Parse AUTO UPDATE or MANUAL UPDATE (MySQL 8.0.27+)
42751        if self.match_texts(&["MANUAL", "AUTO"]) {
42752            let mode = self.previous().text.to_ascii_uppercase();
42753            if self.check(TokenType::Update) {
42754                update_options = Some(Box::new(Expression::Identifier(Identifier::new(mode))));
42755                self.skip(); // consume UPDATE
42756            }
42757        }
42758
42759        Ok(Some(Expression::AnalyzeHistogram(Box::new(
42760            AnalyzeHistogram {
42761                this: Box::new(Expression::Identifier(Identifier::new(action))),
42762                expressions,
42763                expression,
42764                update_options,
42765            },
42766        ))))
42767    }
42768
42769    /// parse_analyze_list - Parses ANALYZE LIST CHAINED ROWS
42770    /// Python: parser.py:8067-8070
42771    pub fn parse_analyze_list(&mut self) -> Result<Option<Expression>> {
42772        if self.match_text_seq(&["CHAINED", "ROWS"]) {
42773            let expression = self.parse_into()?.map(Box::new);
42774            return Ok(Some(Expression::AnalyzeListChainedRows(Box::new(
42775                AnalyzeListChainedRows { expression },
42776            ))));
42777        }
42778        Ok(None)
42779    }
42780
42781    /// parse_analyze_statistics - Parses ANALYZE ... STATISTICS
42782    /// Python: parser.py:8002-8031
42783    pub fn parse_analyze_statistics(&mut self) -> Result<Option<Expression>> {
42784        let kind = self.previous().text.to_ascii_uppercase();
42785        let option = if self.match_text_seq(&["DELTA"]) {
42786            Some(Box::new(Expression::Identifier(Identifier::new(
42787                "DELTA".to_string(),
42788            ))))
42789        } else {
42790            None
42791        };
42792
42793        // Expect STATISTICS keyword
42794        if !self.match_text_seq(&["STATISTICS"]) {
42795            return Ok(None);
42796        }
42797
42798        let mut this: Option<Box<Expression>> = None;
42799        let mut expressions = Vec::new();
42800
42801        if self.match_text_seq(&["NOSCAN"]) {
42802            this = Some(Box::new(Expression::Identifier(Identifier::new(
42803                "NOSCAN".to_string(),
42804            ))));
42805        } else if self.match_token(TokenType::For) {
42806            if self.match_text_seq(&["ALL", "COLUMNS"]) {
42807                this = Some(Box::new(Expression::Identifier(Identifier::new(
42808                    "FOR ALL COLUMNS".to_string(),
42809                ))));
42810            } else if self.match_text_seq(&["COLUMNS"]) {
42811                this = Some(Box::new(Expression::Identifier(Identifier::new(
42812                    "FOR COLUMNS".to_string(),
42813                ))));
42814                // Parse column list
42815                loop {
42816                    if let Some(col) = self.parse_column_reference()? {
42817                        expressions.push(col);
42818                    } else {
42819                        break;
42820                    }
42821                    if !self.match_token(TokenType::Comma) {
42822                        break;
42823                    }
42824                }
42825            }
42826        } else if self.match_text_seq(&["SAMPLE"]) {
42827            // Parse SAMPLE number [PERCENT]
42828            if let Some(sample) = self.parse_number()? {
42829                let sample_kind = if self.match_token(TokenType::Percent) {
42830                    Some("PERCENT".to_string())
42831                } else {
42832                    None
42833                };
42834                expressions.push(Expression::AnalyzeSample(Box::new(AnalyzeSample {
42835                    kind: sample_kind.unwrap_or_default(),
42836                    sample: Some(Box::new(sample)),
42837                })));
42838            }
42839        }
42840
42841        Ok(Some(Expression::AnalyzeStatistics(Box::new(
42842            AnalyzeStatistics {
42843                kind,
42844                option,
42845                this,
42846                expressions,
42847            },
42848        ))))
42849    }
42850
42851    /// parse_analyze_validate - Parses ANALYZE VALIDATE
42852    /// Python: parser.py:8034-8053
42853    pub fn parse_analyze_validate(&mut self) -> Result<Option<Expression>> {
42854        let mut kind = String::new();
42855        let mut this: Option<Box<Expression>> = None;
42856        let mut expression: Option<Box<Expression>> = None;
42857
42858        if self.match_text_seq(&["REF", "UPDATE"]) {
42859            kind = "REF".to_string();
42860            this = Some(Box::new(Expression::Identifier(Identifier::new(
42861                "UPDATE".to_string(),
42862            ))));
42863            if self.match_text_seq(&["SET", "DANGLING", "TO", "NULL"]) {
42864                this = Some(Box::new(Expression::Identifier(Identifier::new(
42865                    "UPDATE SET DANGLING TO NULL".to_string(),
42866                ))));
42867            }
42868        } else if self.match_text_seq(&["STRUCTURE"]) {
42869            kind = "STRUCTURE".to_string();
42870            if self.match_text_seq(&["CASCADE", "FAST"]) {
42871                this = Some(Box::new(Expression::Identifier(Identifier::new(
42872                    "CASCADE FAST".to_string(),
42873                ))));
42874            } else if self.match_text_seq(&["CASCADE", "COMPLETE"]) {
42875                if self.match_texts(&["ONLINE", "OFFLINE"]) {
42876                    let mode = self.previous().text.to_ascii_uppercase();
42877                    this = Some(Box::new(Expression::Identifier(Identifier::new(format!(
42878                        "CASCADE COMPLETE {}",
42879                        mode
42880                    )))));
42881                    expression = self.parse_into()?.map(Box::new);
42882                }
42883            }
42884        }
42885
42886        if kind.is_empty() {
42887            return Ok(None);
42888        }
42889
42890        Ok(Some(Expression::AnalyzeValidate(Box::new(
42891            AnalyzeValidate {
42892                kind,
42893                this,
42894                expression,
42895            },
42896        ))))
42897    }
42898
42899    /// parse_attach_detach - Parses ATTACH/DETACH statements (DuckDB)
42900    /// Python: DuckDB._parse_attach_detach
42901    pub fn parse_attach_detach(&mut self, is_attach: bool) -> Result<Expression> {
42902        // ATTACH [DATABASE] [IF NOT EXISTS] 'path' [AS alias] [(options)]
42903        // DETACH [DATABASE] [IF EXISTS] name
42904        // DATABASE can be tokenized as TokenType::Database (keyword), not just Var
42905        let _ = self.match_identifier("DATABASE") || self.match_token(TokenType::Database);
42906
42907        let exists = if is_attach {
42908            self.match_text_seq(&["IF", "NOT", "EXISTS"])
42909        } else {
42910            self.match_text_seq(&["IF", "EXISTS"])
42911        };
42912
42913        // Parse the expression (can be a path string, identifier, or expression like 'foo' || '.foo2'
42914        // or NOT EXISTS(subquery) for conditional attach)
42915        let this_expr = self.parse_expression()?;
42916
42917        // Check for AS alias
42918        let this = if self.match_token(TokenType::As) {
42919            let alias = self.expect_identifier_or_keyword_with_quoted()?;
42920            Expression::Alias(Box::new(Alias {
42921                this: this_expr,
42922                alias,
42923                column_aliases: Vec::new(),
42924                pre_alias_comments: Vec::new(),
42925                trailing_comments: Vec::new(),
42926                inferred_type: None,
42927            }))
42928        } else {
42929            this_expr
42930        };
42931
42932        if is_attach {
42933            // Parse optional (options)
42934            let expressions = if self.match_token(TokenType::LParen) {
42935                let mut opts = Vec::new();
42936                loop {
42937                    // Parse option: KEY [VALUE]
42938                    let key_name = self.advance().text.to_ascii_uppercase();
42939                    let key = Expression::Identifier(Identifier::new(key_name));
42940                    let value = if !self.check(TokenType::Comma) && !self.check(TokenType::RParen) {
42941                        // The value can be an identifier, string, boolean, etc.
42942                        let val_token = self.advance();
42943                        let val_expr = if val_token.token_type == TokenType::String {
42944                            Expression::Literal(Box::new(Literal::String(val_token.text.clone())))
42945                        } else if val_token.token_type == TokenType::True {
42946                            Expression::Boolean(BooleanLiteral { value: true })
42947                        } else if val_token.token_type == TokenType::False {
42948                            Expression::Boolean(BooleanLiteral { value: false })
42949                        } else {
42950                            Expression::Identifier(Identifier::new(val_token.text.clone()))
42951                        };
42952                        Some(Box::new(val_expr))
42953                    } else {
42954                        None
42955                    };
42956                    opts.push(Expression::AttachOption(Box::new(AttachOption {
42957                        this: Box::new(key),
42958                        expression: value,
42959                    })));
42960                    if !self.match_token(TokenType::Comma) {
42961                        break;
42962                    }
42963                }
42964                self.expect(TokenType::RParen)?;
42965                opts
42966            } else {
42967                Vec::new()
42968            };
42969
42970            Ok(Expression::Attach(Box::new(Attach {
42971                this: Box::new(this),
42972                exists,
42973                expressions,
42974            })))
42975        } else {
42976            Ok(Expression::Detach(Box::new(Detach {
42977                this: Box::new(this),
42978                exists,
42979            })))
42980        }
42981    }
42982
42983    /// parse_install - Parses INSTALL statement (DuckDB)
42984    /// Python: DuckDB._parse_install
42985    pub fn parse_install(&mut self, force: bool) -> Result<Expression> {
42986        // INSTALL extension [FROM source]
42987        let name = self.expect_identifier_or_keyword()?;
42988        let this = Expression::Identifier(Identifier::new(name));
42989
42990        let from_ = if self.match_token(TokenType::From) {
42991            // FROM can be followed by a string or identifier
42992            Some(Box::new(self.parse_primary()?))
42993        } else {
42994            None
42995        };
42996
42997        Ok(Expression::Install(Box::new(Install {
42998            this: Box::new(this),
42999            from_,
43000            force: if force {
43001                Some(Box::new(Expression::Boolean(BooleanLiteral {
43002                    value: true,
43003                })))
43004            } else {
43005                None
43006            },
43007        })))
43008    }
43009
43010    /// parse_force_statement - Parses FORCE INSTALL/CHECKPOINT (DuckDB)
43011    /// Python: DuckDB._parse_force
43012    pub fn parse_force_statement(&mut self) -> Result<Expression> {
43013        if self.match_identifier("INSTALL") {
43014            return self.parse_install(true);
43015        }
43016        // FORCE CHECKPOINT or other: fallback to command
43017        self.parse_as_command()?
43018            .ok_or_else(|| self.parse_error("Failed to parse FORCE statement"))
43019    }
43020
43021    /// parse_summarize_statement - Parses SUMMARIZE statement (DuckDB)
43022    /// Python: DuckDB parser for SUMMARIZE
43023    pub fn parse_summarize_statement(&mut self) -> Result<Expression> {
43024        // SUMMARIZE [TABLE] expression
43025        let is_table = self.match_token(TokenType::Table);
43026
43027        // Try to parse a SELECT statement, string, or table reference
43028        let this = if self.check(TokenType::Select) || self.check(TokenType::With) {
43029            self.parse_select()?
43030        } else if self.check(TokenType::String) {
43031            self.parse_primary()?
43032        } else {
43033            // Parse as table name
43034            self.parse_table_parts()?
43035                .unwrap_or(Expression::Identifier(Identifier::new(String::new())))
43036        };
43037
43038        Ok(Expression::Summarize(Box::new(Summarize {
43039            this: Box::new(this),
43040            table: if is_table {
43041                Some(Box::new(Expression::Boolean(BooleanLiteral {
43042                    value: true,
43043                })))
43044            } else {
43045                None
43046            },
43047        })))
43048    }
43049
43050    /// parse_deallocate_prepare - Parses DEALLOCATE PREPARE <name>
43051    /// Presto/Trino syntax for deallocating prepared statements
43052    pub fn parse_deallocate_prepare(&mut self) -> Result<Expression> {
43053        self.skip(); // consume DEALLOCATE
43054
43055        // Check for PREPARE keyword
43056        if self.match_identifier("PREPARE") {
43057            // Parse the statement name
43058            let name = if !self.is_at_end() && !self.check(TokenType::Semicolon) {
43059                self.advance().text.clone()
43060            } else {
43061                String::new()
43062            };
43063
43064            // Build the command text
43065            let command_text = if name.is_empty() {
43066                "DEALLOCATE PREPARE".to_string()
43067            } else {
43068                format!("DEALLOCATE PREPARE {}", name)
43069            };
43070
43071            Ok(Expression::Command(Box::new(Command {
43072                this: command_text,
43073            })))
43074        } else {
43075            // Just DEALLOCATE without PREPARE - consume rest as command
43076            let mut parts = vec!["DEALLOCATE".to_string()];
43077            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
43078                let token = self.advance();
43079                parts.push(token.text.clone());
43080            }
43081            Ok(Expression::Command(Box::new(Command {
43082                this: parts.join(" "),
43083            })))
43084        }
43085    }
43086
43087    /// parse_as_command - Creates Command expression
43088    #[allow(unused_variables, unused_mut)]
43089    /// parse_as_command - Parses remaining tokens as a raw command
43090    /// Python: _parse_as_command
43091    /// Used as fallback when specific parsing fails
43092    pub fn parse_as_command(&mut self) -> Result<Option<Expression>> {
43093        // Get the starting token text
43094        let start_text = if self.current > 0 {
43095            self.tokens
43096                .get(self.current - 1)
43097                .map(|t| t.text.clone())
43098                .unwrap_or_default()
43099        } else {
43100            String::new()
43101        };
43102
43103        // Consume all remaining tokens, storing both text and type
43104        let mut tokens_info: Vec<(String, TokenType)> = Vec::new();
43105        while !self.is_at_end() {
43106            let token = self.advance();
43107            tokens_info.push((token.text.clone(), token.token_type.clone()));
43108        }
43109
43110        // Join tokens intelligently, avoiding spaces around punctuation
43111        let mut expression = String::new();
43112        for (i, (text, token_type)) in tokens_info.iter().enumerate() {
43113            if i > 0 {
43114                // Check if we should add a space before this token
43115                let prev_type = &tokens_info[i - 1].1;
43116                let needs_space = !Self::is_punctuation_token(prev_type)
43117                    && !Self::is_punctuation_token(token_type);
43118                if needs_space {
43119                    expression.push(' ');
43120                }
43121            }
43122            expression.push_str(text);
43123        }
43124
43125        Ok(Some(Expression::Command(Box::new(Command {
43126            this: if expression.is_empty() {
43127                start_text
43128            } else {
43129                format!("{} {}", start_text, expression)
43130            },
43131        }))))
43132    }
43133
43134    /// Helper to determine if a token type is punctuation that shouldn't have spaces around it
43135    fn is_punctuation_token(token_type: &TokenType) -> bool {
43136        matches!(
43137            token_type,
43138            TokenType::Dot | TokenType::Colon | TokenType::DColon
43139        )
43140    }
43141
43142    /// Fallback to Command expression from a saved position.
43143    /// Extracts verbatim SQL text from source if available, consuming tokens until semicolon/EOF.
43144    fn fallback_to_command(&mut self, start_pos: usize) -> Result<Expression> {
43145        let start_span = self.tokens[start_pos].span.start;
43146        // Consume until semicolon or end
43147        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
43148            self.skip();
43149        }
43150        let command_text = if let Some(ref source) = self.source {
43151            let end_span = if self.current > 0 {
43152                self.tokens[self.current - 1].span.end
43153            } else {
43154                start_span
43155            };
43156            source[start_span..end_span].trim().to_string()
43157        } else {
43158            // Fallback: join token texts
43159            let mut parts = Vec::new();
43160            for i in start_pos..self.current {
43161                if self.tokens[i].token_type == TokenType::String {
43162                    parts.push(format!("'{}'", self.tokens[i].text.replace('\'', "''")));
43163                } else {
43164                    parts.push(self.tokens[i].text.clone());
43165                }
43166            }
43167            parts.join(" ")
43168        };
43169        Ok(Expression::Command(Box::new(Command {
43170            this: command_text,
43171        })))
43172    }
43173
43174    /// parse_assignment - Parses assignment expressions (variable := value)
43175    /// Python: _parse_assignment
43176    pub fn parse_assignment(&mut self) -> Result<Option<Expression>> {
43177        // First parse a disjunction (left side of potential assignment)
43178        let mut this = self.parse_disjunction()?;
43179
43180        // Handle := assignment operator
43181        while self.match_token(TokenType::ColonEq) {
43182            if let Some(left) = this {
43183                let right = self.parse_assignment()?;
43184                if let Some(right_expr) = right {
43185                    this = Some(Expression::PropertyEQ(Box::new(BinaryOp {
43186                        left,
43187                        right: right_expr,
43188                        left_comments: Vec::new(),
43189                        operator_comments: Vec::new(),
43190                        trailing_comments: Vec::new(),
43191                        inferred_type: None,
43192                    })));
43193                } else {
43194                    this = Some(left);
43195                    break;
43196                }
43197            } else {
43198                break;
43199            }
43200        }
43201
43202        // ClickHouse ternary operator: condition ? true_value : false_value
43203        // Parsed as: If(this=condition, true=true_value, false=false_value)
43204        if matches!(
43205            self.config.dialect,
43206            Some(crate::dialects::DialectType::ClickHouse)
43207        ) {
43208            if let Some(condition) = this {
43209                if self.match_token(TokenType::Parameter) {
43210                    if self.check(TokenType::Colon) {
43211                        return Err(self.parse_error(
43212                            "Expected true expression after ? in ClickHouse ternary",
43213                        ));
43214                    }
43215                    let true_value = self.parse_assignment()?.ok_or_else(|| {
43216                        self.parse_error("Expected true expression after ? in ClickHouse ternary")
43217                    })?;
43218                    let false_value = if self.match_token(TokenType::Colon) {
43219                        self.parse_assignment()?.unwrap_or(Expression::Null(Null))
43220                    } else {
43221                        Expression::Null(Null)
43222                    };
43223                    return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
43224                        original_name: None,
43225                        condition,
43226                        true_value,
43227                        false_value: Some(false_value),
43228                        inferred_type: None,
43229                    }))));
43230                }
43231                this = Some(condition);
43232            }
43233        }
43234
43235        Ok(this)
43236    }
43237
43238    /// parse_auto_increment - Implemented from Python _parse_auto_increment
43239    /// Calls: parse_bitwise
43240    #[allow(unused_variables, unused_mut)]
43241    pub fn parse_auto_increment(&mut self) -> Result<Option<Expression>> {
43242        if self.match_text_seq(&["START"]) {
43243            return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
43244                Box::new(GeneratedAsIdentityColumnConstraint {
43245                    this: None,
43246                    expression: None,
43247                    on_null: None,
43248                    start: None,
43249                    increment: None,
43250                    minvalue: None,
43251                    maxvalue: None,
43252                    cycle: None,
43253                    order: None,
43254                }),
43255            )));
43256        }
43257        if self.match_text_seq(&["INCREMENT"]) {
43258            // Matched: INCREMENT
43259            return Ok(None);
43260        }
43261        if self.match_text_seq(&["ORDER"]) {
43262            // Matched: ORDER
43263            return Ok(None);
43264        }
43265        Ok(None)
43266    }
43267
43268    /// parse_auto_property - Implemented from Python _parse_auto_property
43269    #[allow(unused_variables, unused_mut)]
43270    pub fn parse_auto_property(&mut self) -> Result<Option<Expression>> {
43271        if self.match_text_seq(&["REFRESH"]) {
43272            // Matched: REFRESH
43273            return Ok(None);
43274        }
43275        Ok(None)
43276    }
43277
43278    /// parse_between - Implemented from Python _parse_between
43279    #[allow(unused_variables, unused_mut)]
43280    pub fn parse_between(&mut self) -> Result<Option<Expression>> {
43281        if self.match_text_seq(&["SYMMETRIC"]) {
43282            // Matched: SYMMETRIC
43283            return Ok(None);
43284        }
43285        if self.match_text_seq(&["ASYMMETRIC"]) {
43286            // Matched: ASYMMETRIC
43287            return Ok(None);
43288        }
43289        Ok(None)
43290    }
43291
43292    /// parse_bitwise - Parses bitwise OR/XOR/AND expressions
43293    /// Python: _parse_bitwise
43294    /// Delegates to the existing parse_bitwise_or in the operator precedence chain
43295    pub fn parse_bitwise(&mut self) -> Result<Option<Expression>> {
43296        let start = self.current;
43297        match self.parse_bitwise_or() {
43298            Ok(expr) => Ok(Some(expr)),
43299            Err(_err) if self.current == start => Ok(None),
43300            Err(err) => Err(err),
43301        }
43302    }
43303
43304    /// parse_blockcompression - Implemented from Python _parse_blockcompression
43305    #[allow(unused_variables, unused_mut)]
43306    pub fn parse_blockcompression(&mut self) -> Result<Option<Expression>> {
43307        if self.match_text_seq(&["ALWAYS"]) {
43308            return Ok(Some(Expression::BlockCompressionProperty(Box::new(
43309                BlockCompressionProperty {
43310                    autotemp: None,
43311                    always: None,
43312                    default: None,
43313                    manual: None,
43314                    never: None,
43315                },
43316            ))));
43317        }
43318        if self.match_text_seq(&["MANUAL"]) {
43319            // Matched: MANUAL
43320            return Ok(None);
43321        }
43322        Ok(None)
43323    }
43324
43325    /// parse_boolean - Parse boolean literal (TRUE/FALSE)
43326    /// Python: if self._match(TokenType.TRUE): return exp.Boolean(this=True)
43327    pub fn parse_boolean(&mut self) -> Result<Option<Expression>> {
43328        if self.match_token(TokenType::True) {
43329            return Ok(Some(Expression::Boolean(BooleanLiteral { value: true })));
43330        }
43331        if self.match_token(TokenType::False) {
43332            return Ok(Some(Expression::Boolean(BooleanLiteral { value: false })));
43333        }
43334        Ok(None)
43335    }
43336
43337    /// parse_bracket - Ported from Python _parse_bracket
43338    /// Parses bracket expressions: array[index], array literal [1,2,3], or struct {key: value}
43339    #[allow(unused_variables, unused_mut)]
43340    pub fn parse_bracket(&mut self) -> Result<Option<Expression>> {
43341        self.parse_bracket_with_expr(None)
43342    }
43343
43344    /// parse_bracket_with_expr - Parses bracket with optional left-side expression
43345    fn parse_bracket_with_expr(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
43346        // Check for [ or {
43347        let is_bracket = self.match_token(TokenType::LBracket);
43348        let is_brace = if !is_bracket {
43349            self.match_token(TokenType::LBrace)
43350        } else {
43351            false
43352        };
43353
43354        if !is_bracket && !is_brace {
43355            return Ok(this);
43356        }
43357
43358        // Parse comma-separated expressions inside brackets
43359        let mut expressions: Vec<Expression> = Vec::new();
43360
43361        if is_bracket && !self.check(TokenType::RBracket) {
43362            // Check for slice syntax at the start: [:...] or [:-...]
43363            // This needs to be detected before parse_bracket_key_value which calls parse_primary
43364            // and parse_primary would consume : as a parameter prefix
43365            let first_expr = if self.check(TokenType::Colon) {
43366                // This is slice syntax like [:] or [:-1] or [::step]
43367                // Parse it using slice parser with no 'this'
43368                if let Some(slice) = self.parse_slice()? {
43369                    slice
43370                } else {
43371                    self.parse_expression()?
43372                }
43373            } else if let Ok(Some(expr)) = self.parse_bracket_key_value() {
43374                expr
43375            } else {
43376                // Parse regular expression and check for slice
43377                let expr = self.parse_expression()?;
43378                // Check if followed by colon (slice syntax like [start:end])
43379                if self.check(TokenType::Colon) {
43380                    if let Some(slice) = self.parse_slice_with_this(Some(expr))? {
43381                        slice
43382                    } else {
43383                        return Err(self.parse_error("Failed to parse slice"));
43384                    }
43385                } else {
43386                    expr
43387                }
43388            };
43389
43390            // Check for comprehension syntax: [expr FOR var IN iterator [IF condition]]
43391            if self.match_token(TokenType::For) {
43392                // Parse loop variable - typically a simple identifier like 'x'
43393                let loop_var = self.parse_primary()?;
43394
43395                // Parse optional position (second variable after comma)
43396                let position = if self.match_token(TokenType::Comma) {
43397                    Some(self.parse_primary()?)
43398                } else {
43399                    None
43400                };
43401
43402                // Expect IN keyword
43403                if !self.match_token(TokenType::In) {
43404                    return Err(self.parse_error("Expected IN in comprehension"));
43405                }
43406
43407                // Parse iterator expression
43408                let iterator = self.parse_expression()?;
43409
43410                // Parse optional condition after IF
43411                let condition = if self.match_token(TokenType::If) {
43412                    Some(self.parse_expression()?)
43413                } else {
43414                    None
43415                };
43416
43417                // Expect closing bracket
43418                self.expect(TokenType::RBracket)?;
43419
43420                // Return Comprehension wrapped in an expression
43421                return Ok(Some(Expression::Comprehension(Box::new(Comprehension {
43422                    this: Box::new(first_expr),
43423                    expression: Box::new(loop_var),
43424                    position: position.map(Box::new),
43425                    iterator: Some(Box::new(iterator)),
43426                    condition: condition.map(Box::new),
43427                }))));
43428            }
43429
43430            expressions.push(first_expr);
43431
43432            // Continue parsing remaining expressions
43433            while self.match_token(TokenType::Comma) {
43434                if let Ok(Some(expr)) = self.parse_bracket_key_value() {
43435                    expressions.push(expr);
43436                } else {
43437                    match self.parse_expression() {
43438                        Ok(expr) => expressions.push(expr),
43439                        Err(_) => break,
43440                    }
43441                }
43442            }
43443        } else if is_brace && !self.check(TokenType::RBrace) {
43444            loop {
43445                if let Ok(Some(expr)) = self.parse_bracket_key_value() {
43446                    expressions.push(expr);
43447                } else {
43448                    match self.parse_expression() {
43449                        Ok(expr) => expressions.push(expr),
43450                        Err(_) => break,
43451                    }
43452                }
43453                if !self.match_token(TokenType::Comma) {
43454                    break;
43455                }
43456            }
43457        }
43458
43459        // Expect closing bracket
43460        if is_bracket {
43461            self.expect(TokenType::RBracket)?;
43462        } else if is_brace {
43463            self.expect(TokenType::RBrace)?;
43464        }
43465
43466        // Build the result
43467        if is_brace {
43468            // Struct literal: {key: value, ...}
43469            // Convert expressions to (Option<name>, expr) pairs
43470            let fields: Vec<(Option<String>, Expression)> =
43471                expressions.into_iter().map(|e| (None, e)).collect();
43472            Ok(Some(Expression::Struct(Box::new(Struct { fields }))))
43473        } else if let Some(base_expr) = this {
43474            // Subscript access: base[index]
43475            if expressions.len() == 1 {
43476                Ok(Some(Expression::Subscript(Box::new(Subscript {
43477                    this: base_expr,
43478                    index: expressions.remove(0),
43479                }))))
43480            } else {
43481                // Multiple indices - create nested subscripts or array
43482                let mut result = base_expr;
43483                for expr in expressions {
43484                    result = Expression::Subscript(Box::new(Subscript {
43485                        this: result,
43486                        index: expr,
43487                    }));
43488                }
43489                Ok(Some(result))
43490            }
43491        } else {
43492            // Array literal: [1, 2, 3]
43493            Ok(Some(Expression::Array(Box::new(Array { expressions }))))
43494        }
43495    }
43496
43497    /// parse_bracket_key_value - Ported from Python _parse_bracket_key_value
43498    /// Parses key-value pairs in brackets: key: value or key => value
43499    #[allow(unused_variables, unused_mut)]
43500    pub fn parse_bracket_key_value(&mut self) -> Result<Option<Expression>> {
43501        let saved_pos = self.current;
43502
43503        // Try to parse as key: value or key => value
43504        if let Ok(key) = self.parse_primary() {
43505            // Check for : or =>
43506            if self.match_token(TokenType::Colon) || self.match_text_seq(&["=>"]) {
43507                match self.parse_expression() {
43508                    Ok(value) => {
43509                        // Return as NamedArgument for key-value pair
43510                        // Extract the name from the key (identifier or string literal)
43511                        let name = match &key {
43512                            Expression::Identifier(id) => id.clone(),
43513                            Expression::Literal(lit)
43514                                if matches!(
43515                                    lit.as_ref(),
43516                                    crate::expressions::Literal::String(s)
43517                                ) =>
43518                            {
43519                                let crate::expressions::Literal::String(s) = lit.as_ref() else {
43520                                    unreachable!()
43521                                };
43522                                Identifier::new(s.clone())
43523                            }
43524                            _ => Identifier::new("".to_string()),
43525                        };
43526                        return Ok(Some(Expression::NamedArgument(Box::new(NamedArgument {
43527                            name,
43528                            value,
43529                            separator: NamedArgSeparator::DArrow, // Using DArrow for colon-style key: value
43530                        }))));
43531                    }
43532                    Err(_) => {
43533                        self.current = saved_pos;
43534                        return Ok(None);
43535                    }
43536                }
43537            }
43538            self.current = saved_pos;
43539        }
43540
43541        Ok(None)
43542    }
43543
43544    /// parse_ceil_floor - Implemented from Python _parse_ceil_floor
43545    /// Calls: parse_lambda, parse_var
43546    #[allow(unused_variables, unused_mut)]
43547    pub fn parse_ceil_floor(&mut self) -> Result<Option<Expression>> {
43548        if self.match_text_seq(&["TO"]) {
43549            // Matched: TO
43550            return Ok(None);
43551        }
43552        Ok(None)
43553    }
43554
43555    /// parse_changes - Implemented from Python _parse_changes
43556    /// Parses: CHANGES(INFORMATION => var) AT|BEFORE(...) END(...)
43557    pub fn parse_changes(&mut self) -> Result<Option<Expression>> {
43558        // Match: CHANGES(INFORMATION =>
43559        if !self.match_text_seq(&["CHANGES", "(", "INFORMATION", "=>"]) {
43560            return Ok(None);
43561        }
43562
43563        // Parse information (any token as var)
43564        let information = self.parse_var()?.map(Box::new);
43565
43566        // Match closing paren
43567        self.match_token(TokenType::RParen);
43568
43569        // Parse at_before (Snowflake AT/BEFORE clause)
43570        let at_before = self.parse_historical_data()?.map(Box::new);
43571
43572        // Parse end (optional second historical data clause)
43573        let end = self.parse_historical_data()?.map(Box::new);
43574
43575        Ok(Some(Expression::Changes(Box::new(Changes {
43576            information,
43577            at_before,
43578            end,
43579        }))))
43580    }
43581
43582    /// parse_char - Parses CHAR/CHR function with optional USING charset
43583    /// Python: CHAR(args...) [USING charset]
43584    /// MySQL: CHAR(n1, n2, ... USING charset)
43585    pub fn parse_char(&mut self) -> Result<Option<Expression>> {
43586        // Parse expressions inside CHAR()
43587        let mut args = Vec::new();
43588        loop {
43589            let expr = self.parse_expression()?;
43590            args.push(expr);
43591            if !self.match_token(TokenType::Comma) {
43592                break;
43593            }
43594        }
43595
43596        // Check for USING charset
43597        let charset = if self.match_token(TokenType::Using) {
43598            self.parse_var()?.map(|v| {
43599                if let Expression::Identifier(id) = v {
43600                    id.name
43601                } else {
43602                    String::new()
43603                }
43604            })
43605        } else {
43606            None
43607        };
43608
43609        if args.is_empty() {
43610            return Ok(None);
43611        }
43612
43613        // If there's a charset or multiple args, use CharFunc (MySQL-style)
43614        // Otherwise use simple Chr for single-arg CHR function
43615        if charset.is_some() || args.len() > 1 {
43616            Ok(Some(Expression::CharFunc(Box::new(
43617                crate::expressions::CharFunc {
43618                    args,
43619                    charset,
43620                    name: None, // defaults to CHAR
43621                },
43622            ))))
43623        } else {
43624            Ok(Some(Expression::Chr(Box::new(UnaryFunc::new(
43625                args.into_iter().next().unwrap(),
43626            )))))
43627        }
43628    }
43629
43630    /// parse_character_set - Ported from Python _parse_character_set
43631    #[allow(unused_variables, unused_mut)]
43632    /// parse_character_set - Parses CHARACTER SET property
43633    /// Example: CHARACTER SET = utf8 or CHARACTER SET utf8mb4
43634    pub fn parse_character_set(&mut self) -> Result<Option<Expression>> {
43635        // Optional = sign
43636        self.match_token(TokenType::Eq);
43637
43638        // Parse the charset name (variable or string)
43639        let charset = self.parse_var_or_string()?;
43640        if charset.is_none() {
43641            return Ok(None);
43642        }
43643
43644        Ok(Some(Expression::CharacterSetProperty(Box::new(
43645            CharacterSetProperty {
43646                this: Box::new(charset.unwrap()),
43647                default: None,
43648            },
43649        ))))
43650    }
43651
43652    /// parse_checksum - Implemented from Python _parse_checksum
43653    #[allow(unused_variables, unused_mut)]
43654    pub fn parse_checksum(&mut self) -> Result<Option<Expression>> {
43655        if self.match_text_seq(&["OFF"]) {
43656            return Ok(Some(Expression::ChecksumProperty(Box::new(
43657                ChecksumProperty {
43658                    on: None,
43659                    default: None,
43660                },
43661            ))));
43662        }
43663        Ok(None)
43664    }
43665
43666    /// parse_cluster - CLUSTER BY clause for Hive/Spark-style queries
43667    /// Parses a list of ordered expressions (columns with optional ASC/DESC)
43668    #[allow(unused_variables, unused_mut)]
43669    pub fn parse_cluster(&mut self) -> Result<Option<Expression>> {
43670        let mut expressions: Vec<Ordered> = Vec::new();
43671
43672        loop {
43673            // Parse an ordered expression (column with optional direction)
43674            if let Some(ordered) = self.parse_ordered_item()? {
43675                expressions.push(ordered);
43676            } else {
43677                break;
43678            }
43679
43680            if !self.match_token(TokenType::Comma) {
43681                break;
43682            }
43683        }
43684
43685        if expressions.is_empty() {
43686            return Ok(None);
43687        }
43688
43689        Ok(Some(Expression::ClusterBy(Box::new(ClusterBy {
43690            expressions,
43691        }))))
43692    }
43693
43694    /// parse_clustered_by - Implemented from Python _parse_clustered_by
43695    #[allow(unused_variables, unused_mut)]
43696    pub fn parse_clustered_by(&mut self) -> Result<Option<Expression>> {
43697        if self.match_text_seq(&["BY"]) {
43698            return Ok(Some(Expression::ClusteredByProperty(Box::new(
43699                ClusteredByProperty {
43700                    expressions: Vec::new(),
43701                    sorted_by: None,
43702                    buckets: None,
43703                },
43704            ))));
43705        }
43706        if self.match_text_seq(&["SORTED", "BY"]) {
43707            // Matched: SORTED BY
43708            return Ok(None);
43709        }
43710        Ok(None)
43711    }
43712
43713    /// Parse Snowflake colon JSON path extraction: data:field or data:field.subfield
43714    /// Python: def _parse_colon_as_variant_extract(self, this)
43715    pub fn parse_colon_as_variant_extract(
43716        &mut self,
43717        this: Expression,
43718    ) -> Result<Option<Expression>> {
43719        // Build a JSON path from colon-separated identifiers
43720        // Track whether each segment was quoted (needs bracket notation for spaces/special chars)
43721        let mut json_path: Vec<(String, bool)> = Vec::new();
43722
43723        while self.match_token(TokenType::Colon) {
43724            // Parse the path segment (field name)
43725            if let Some(field) = self.parse_identifier()? {
43726                if let Expression::Identifier(ident) = field {
43727                    json_path.push((
43728                        ident.name.clone(),
43729                        ident.quoted || ident.name.contains(' ') || ident.name.contains('\''),
43730                    ));
43731                }
43732            }
43733
43734            // Check for dot-separated sub-paths
43735            while self.match_token(TokenType::Dot) {
43736                if let Some(subfield) = self.parse_identifier()? {
43737                    if let Expression::Identifier(ident) = subfield {
43738                        json_path.push((
43739                            ident.name.clone(),
43740                            ident.quoted || ident.name.contains(' ') || ident.name.contains('\''),
43741                        ));
43742                    }
43743                }
43744            }
43745        }
43746
43747        if json_path.is_empty() {
43748            return Ok(Some(this));
43749        }
43750
43751        // Build the JSON path expression string
43752        // Use bracket notation for segments with spaces/special chars: a["b c"]
43753        // Use dot notation for simple segments: a.b.c
43754        let mut path_str = String::new();
43755        for (i, (segment, needs_bracket)) in json_path.iter().enumerate() {
43756            if *needs_bracket {
43757                // Bracket notation: ["key with spaces"]
43758                path_str.push('[');
43759                path_str.push('"');
43760                path_str.push_str(segment);
43761                path_str.push('"');
43762                path_str.push(']');
43763            } else {
43764                if i > 0 {
43765                    path_str.push('.');
43766                }
43767                path_str.push_str(segment);
43768            }
43769        }
43770
43771        Ok(Some(Expression::JSONExtract(Box::new(JSONExtract {
43772            this: Box::new(this),
43773            expression: Box::new(Expression::Literal(Box::new(Literal::String(path_str)))),
43774            only_json_types: None,
43775            expressions: Vec::new(),
43776            variant_extract: Some(Box::new(Expression::Boolean(BooleanLiteral {
43777                value: true,
43778            }))),
43779            json_query: None,
43780            option: None,
43781            quote: None,
43782            on_condition: None,
43783            requires_json: None,
43784        }))))
43785    }
43786
43787    /// parse_column - Parse column expression
43788    /// Python: this = self._parse_column_reference(); return self._parse_column_ops(this)
43789    pub fn parse_column(&mut self) -> Result<Option<Expression>> {
43790        // Parse column reference (field name that becomes a Column expression)
43791        let column_ref = self.parse_column_reference()?;
43792        if column_ref.is_some() {
43793            // Apply column ops (bracket subscript, property access with dots, casts)
43794            return self.parse_column_ops_with_expr(column_ref);
43795        }
43796        // Try parsing bracket directly if no column reference
43797        self.parse_bracket()
43798    }
43799
43800    /// parse_column_constraint - Ported from Python _parse_column_constraint
43801    /// Parses column-level constraints like NOT NULL, PRIMARY KEY, UNIQUE, DEFAULT, CHECK, etc.
43802    #[allow(unused_variables, unused_mut)]
43803    pub fn parse_column_constraint(&mut self) -> Result<Option<Expression>> {
43804        // Check for optional CONSTRAINT keyword and name
43805        let constraint_name = if self.match_token(TokenType::Constraint) {
43806            self.parse_id_var()?.and_then(|e| {
43807                if let Expression::Identifier(id) = e {
43808                    Some(id)
43809                } else {
43810                    None
43811                }
43812            })
43813        } else {
43814            None
43815        };
43816
43817        // NOT NULL
43818        if self.match_text_seq(&["NOT", "NULL"]) {
43819            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
43820                NotNullColumnConstraint { allow_null: None },
43821            ))));
43822        }
43823
43824        // NOT FOR REPLICATION (SQL Server) - must be before NULL check
43825        if self.match_text_seq(&["NOT", "FOR", "REPLICATION"]) {
43826            return Ok(Some(Expression::Property(Box::new(
43827                crate::expressions::Property {
43828                    this: Box::new(Expression::Identifier(Identifier::new(
43829                        "NOT FOR REPLICATION".to_string(),
43830                    ))),
43831                    value: None,
43832                },
43833            ))));
43834        }
43835
43836        // NULL
43837        if self.match_text_seq(&["NULL"]) {
43838            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
43839                NotNullColumnConstraint {
43840                    allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
43841                        value: true,
43842                    }))),
43843                },
43844            ))));
43845        }
43846
43847        // PRIMARY KEY
43848        if self.match_text_seq(&["PRIMARY", "KEY"]) {
43849            return Ok(Some(Expression::PrimaryKeyColumnConstraint(Box::new(
43850                PrimaryKeyColumnConstraint {
43851                    desc: None,
43852                    options: Vec::new(),
43853                },
43854            ))));
43855        }
43856
43857        // UNIQUE
43858        if self.match_text_seq(&["UNIQUE"]) {
43859            // Check for optional KEY/INDEX
43860            let _ = self.match_texts(&["KEY", "INDEX"]);
43861            // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
43862            let nulls = if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
43863                Some(Box::new(Expression::Boolean(BooleanLiteral {
43864                    value: true,
43865                })))
43866            } else {
43867                None
43868            };
43869            return Ok(Some(Expression::UniqueColumnConstraint(Box::new(
43870                UniqueColumnConstraint {
43871                    this: None,
43872                    index_type: None,
43873                    on_conflict: None,
43874                    nulls,
43875                    options: Vec::new(),
43876                },
43877            ))));
43878        }
43879
43880        // DEFAULT
43881        if self.match_text_seq(&["DEFAULT"]) {
43882            let default_value = self.parse_select_or_expression()?;
43883            if let Some(val) = default_value {
43884                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
43885                    DefaultColumnConstraint {
43886                        this: Box::new(val),
43887                        for_column: None,
43888                    },
43889                ))));
43890            }
43891            return Ok(None);
43892        }
43893
43894        // CHECK
43895        if self.match_text_seq(&["CHECK"]) {
43896            if self.match_token(TokenType::LParen) {
43897                let expr = self.parse_select_or_expression()?;
43898                self.match_token(TokenType::RParen);
43899                if let Some(check_expr) = expr {
43900                    return Ok(Some(Expression::CheckColumnConstraint(Box::new(
43901                        CheckColumnConstraint {
43902                            this: Box::new(check_expr),
43903                            enforced: None,
43904                        },
43905                    ))));
43906                }
43907            }
43908            return Ok(None);
43909        }
43910
43911        // REFERENCES (foreign key)
43912        if self.match_text_seq(&["REFERENCES"]) {
43913            let table = self.parse_table_parts()?;
43914            let columns = if self.match_token(TokenType::LParen) {
43915                let mut cols = Vec::new();
43916                loop {
43917                    if let Some(col) = self.parse_id_var()? {
43918                        cols.push(col);
43919                    }
43920                    if !self.match_token(TokenType::Comma) {
43921                        break;
43922                    }
43923                }
43924                self.match_token(TokenType::RParen);
43925                cols
43926            } else {
43927                Vec::new()
43928            };
43929
43930            return Ok(Some(Expression::ForeignKey(Box::new(ForeignKey {
43931                expressions: columns,
43932                reference: table.map(Box::new),
43933                delete: None,
43934                update: None,
43935                options: Vec::new(),
43936            }))));
43937        }
43938
43939        // AUTO_INCREMENT / AUTOINCREMENT / IDENTITY
43940        if self.match_texts(&["AUTO_INCREMENT", "AUTOINCREMENT", "IDENTITY"]) {
43941            // Check for IDENTITY(start, increment) or IDENTITY START x INCREMENT y syntax
43942            let mut start = None;
43943            let mut increment = None;
43944
43945            if self.match_token(TokenType::LParen) {
43946                // Parse (start, increment)
43947                start = self.parse_bitwise()?;
43948                if self.match_token(TokenType::Comma) {
43949                    increment = self.parse_bitwise()?;
43950                }
43951                self.expect(TokenType::RParen)?;
43952            } else if self.match_text_seq(&["START"]) {
43953                // Parse START x INCREMENT y
43954                start = self.parse_bitwise()?;
43955                if self.match_text_seq(&["INCREMENT"]) {
43956                    increment = self.parse_bitwise()?;
43957                }
43958            }
43959
43960            if start.is_some() || increment.is_some() {
43961                return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
43962                    Box::new(GeneratedAsIdentityColumnConstraint {
43963                        this: Some(Box::new(Expression::Boolean(BooleanLiteral {
43964                            value: false,
43965                        }))),
43966                        expression: None,
43967                        on_null: None,
43968                        start: start.map(Box::new),
43969                        increment: increment.map(Box::new),
43970                        minvalue: None,
43971                        maxvalue: None,
43972                        cycle: None,
43973                        order: None,
43974                    }),
43975                )));
43976            }
43977            return Ok(Some(Expression::AutoIncrementColumnConstraint(
43978                AutoIncrementColumnConstraint,
43979            )));
43980        }
43981
43982        // COMMENT 'text' - CommentColumnConstraint is a unit struct, use a different expression
43983        if self.match_text_seq(&["COMMENT"]) {
43984            if let Some(comment) = self.parse_string()? {
43985                // Use CommentColumnConstraint unit struct
43986                return Ok(Some(Expression::CommentColumnConstraint(
43987                    CommentColumnConstraint,
43988                )));
43989            }
43990            return Ok(None);
43991        }
43992
43993        // COLLATE collation_name - use CollateProperty instead
43994        if self.match_text_seq(&["COLLATE"]) {
43995            if let Some(collation) = self.parse_id_var()? {
43996                return Ok(Some(Expression::CollateProperty(Box::new(
43997                    CollateProperty {
43998                        this: Box::new(collation),
43999                        default: None,
44000                    },
44001                ))));
44002            }
44003            return Ok(None);
44004        }
44005
44006        // ClickHouse dictionary column attributes: HIERARCHICAL, IS_OBJECT_ID, INJECTIVE
44007        if matches!(
44008            self.config.dialect,
44009            Some(crate::dialects::DialectType::ClickHouse)
44010        ) {
44011            if self.match_texts(&["HIERARCHICAL", "IS_OBJECT_ID", "INJECTIVE"]) {
44012                let attr_name = self.previous().text.to_ascii_uppercase();
44013                return Ok(Some(Expression::Property(Box::new(
44014                    crate::expressions::Property {
44015                        this: Box::new(Expression::Identifier(Identifier::new(attr_name))),
44016                        value: None,
44017                    },
44018                ))));
44019            }
44020            // ClickHouse EXPRESSION expr and ALIAS expr (dictionary column attributes)
44021            if self.match_texts(&["EXPRESSION"]) {
44022                let expr = self.parse_expression()?;
44023                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
44024                    DefaultColumnConstraint {
44025                        this: Box::new(expr),
44026                        for_column: None,
44027                    },
44028                ))));
44029            }
44030        }
44031
44032        // GENERATED ... AS IDENTITY
44033        if self.match_text_seq(&["GENERATED"]) {
44034            let always = self.match_text_seq(&["ALWAYS"]);
44035            if !always {
44036                self.match_text_seq(&["BY", "DEFAULT"]);
44037            }
44038            let on_null = self.match_text_seq(&["ON", "NULL"]);
44039            if self.match_text_seq(&["AS", "IDENTITY"]) {
44040                return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
44041                    Box::new(GeneratedAsIdentityColumnConstraint {
44042                        this: None,
44043                        expression: None,
44044                        on_null: if on_null {
44045                            Some(Box::new(Expression::Boolean(BooleanLiteral {
44046                                value: true,
44047                            })))
44048                        } else {
44049                            None
44050                        },
44051                        start: None,
44052                        increment: None,
44053                        minvalue: None,
44054                        maxvalue: None,
44055                        cycle: None,
44056                        order: None,
44057                    }),
44058                )));
44059            }
44060            return Ok(None);
44061        }
44062
44063        // PATH 'xpath' - for XMLTABLE/JSON_TABLE columns
44064        if self.match_text_seq(&["PATH"]) {
44065            if let Some(path_expr) = self.parse_string()? {
44066                return Ok(Some(Expression::PathColumnConstraint(Box::new(
44067                    PathColumnConstraint {
44068                        this: Box::new(path_expr),
44069                    },
44070                ))));
44071            }
44072            return Ok(None);
44073        }
44074
44075        // Return the constraint name if we matched CONSTRAINT but no actual constraint
44076        if let Some(name) = constraint_name {
44077            return Ok(Some(Expression::Identifier(name)));
44078        }
44079
44080        Ok(None)
44081    }
44082
44083    /// parse_column_def_with_exists - Ported from Python _parse_column_def_with_exists
44084    /// Parses a column definition with optional IF [NOT] EXISTS clause
44085    #[allow(unused_variables, unused_mut)]
44086    pub fn parse_column_def_with_exists(&mut self) -> Result<Option<Expression>> {
44087        let start = self.current;
44088
44089        // Optionally match COLUMN keyword
44090        let _ = self.match_text_seq(&["COLUMN"]);
44091
44092        // Check for IF NOT EXISTS
44093        let not_exists = self.match_text_seq(&["IF", "NOT", "EXISTS"]);
44094        let exists = if !not_exists {
44095            self.match_text_seq(&["IF", "EXISTS"])
44096        } else {
44097            false
44098        };
44099
44100        // Parse the field definition
44101        let expression = self.parse_field_def()?;
44102
44103        if expression.is_none() {
44104            self.current = start;
44105            return Ok(None);
44106        }
44107
44108        // If it's a ColumnDef, we're good
44109        if let Some(Expression::ColumnDef(ref _col_def)) = expression {
44110            // The exists flag would be set on the ColumnDef, but our struct doesn't have that field
44111            // Just return the expression as-is
44112            return Ok(expression);
44113        }
44114
44115        // Not a ColumnDef, backtrack
44116        self.current = start;
44117        Ok(None)
44118    }
44119
44120    /// parse_column_ops - Parses column operations (stub for compatibility)
44121    pub fn parse_column_ops(&mut self) -> Result<Option<Expression>> {
44122        self.parse_column_ops_with_expr(None)
44123    }
44124
44125    /// parse_column_ops_with_expr - Parses column operations (dot access, brackets, casts)
44126    /// Python: _parse_column_ops(this)
44127    pub fn parse_column_ops_with_expr(
44128        &mut self,
44129        this: Option<Expression>,
44130    ) -> Result<Option<Expression>> {
44131        // First apply any bracket subscripts
44132        let mut result = if let Some(expr) = this {
44133            if self.match_token(TokenType::LBracket) {
44134                let index = self.parse_disjunction()?;
44135                self.match_token(TokenType::RBracket);
44136                if let Some(idx) = index {
44137                    Some(Expression::Subscript(Box::new(Subscript {
44138                        this: expr,
44139                        index: idx,
44140                    })))
44141                } else {
44142                    Some(expr)
44143                }
44144            } else {
44145                Some(expr)
44146            }
44147        } else {
44148            None
44149        };
44150
44151        // Handle DOT for qualified column names: table.column or schema.table.column
44152        while self.match_token(TokenType::Dot) {
44153            if result.is_none() {
44154                break;
44155            }
44156            // Handle .* (qualified star) with modifiers
44157            if self.match_token(TokenType::Star) {
44158                // Determine table name from the expression
44159                let table_name = match &result {
44160                    Some(Expression::Column(col)) if col.table.is_none() => Some(col.name.clone()),
44161                    Some(Expression::Dot(dot)) => {
44162                        // For deep qualified names like schema.table.*, use the whole expression name
44163                        fn dot_to_name(expr: &Expression) -> String {
44164                            match expr {
44165                                Expression::Column(col) => {
44166                                    if let Some(ref table) = col.table {
44167                                        format!("{}.{}", table.name, col.name.name)
44168                                    } else {
44169                                        col.name.name.clone()
44170                                    }
44171                                }
44172                                Expression::Dot(d) => {
44173                                    format!("{}.{}", dot_to_name(&d.this), d.field.name)
44174                                }
44175                                _ => String::new(),
44176                            }
44177                        }
44178                        Some(Identifier::new(dot_to_name(&Expression::Dot(dot.clone()))))
44179                    }
44180                    _ => None,
44181                };
44182                let star = self.parse_star_modifiers(table_name)?;
44183                result = Some(Expression::Star(star));
44184                break;
44185            }
44186            // Parse the field identifier - use is_identifier_or_keyword_token to allow keywords
44187            // like "schema" as field names in dot access
44188            // ClickHouse: also allow numeric tuple index access like expr.1, expr.2
44189            if self.is_identifier_or_keyword_token()
44190                || self.check(TokenType::QuotedIdentifier)
44191                || (matches!(
44192                    self.config.dialect,
44193                    Some(crate::dialects::DialectType::ClickHouse)
44194                ) && self.check(TokenType::Number))
44195            {
44196                let token = self.advance();
44197                let field_ident = Identifier {
44198                    name: token.text,
44199                    quoted: token.token_type == TokenType::QuotedIdentifier,
44200                    trailing_comments: Vec::new(),
44201                    span: None,
44202                };
44203                result = Some(Expression::Dot(Box::new(DotAccess {
44204                    this: result.take().unwrap(),
44205                    field: field_ident,
44206                })));
44207            } else {
44208                break;
44209            }
44210        }
44211
44212        // Handle EXCLAMATION for Snowflake model attribute syntax: model!PREDICT(...)
44213        if self.match_token(TokenType::Exclamation) {
44214            if let Some(expr) = result.take() {
44215                // Parse the attribute/function after the exclamation mark
44216                // This can be either a simple identifier (model!admin) or a function call (model!PREDICT(1))
44217                let attr = self.parse_unary()?;
44218                result = Some(Expression::ModelAttribute(Box::new(ModelAttribute {
44219                    this: Box::new(expr),
44220                    expression: Box::new(attr),
44221                })));
44222            }
44223        }
44224
44225        // Handle DCOLON for casts (PostgreSQL syntax: column::type)
44226        if self.match_token(TokenType::DColon) {
44227            if let Some(type_expr) = self.parse_types()? {
44228                if let Some(expr) = result {
44229                    // Extract DataType from the expression
44230                    let data_type = match type_expr {
44231                        Expression::DataType(dt) => dt,
44232                        _ => {
44233                            result = Some(expr);
44234                            return Ok(result);
44235                        }
44236                    };
44237                    result = Some(Expression::Cast(Box::new(Cast {
44238                        this: expr,
44239                        to: data_type,
44240                        trailing_comments: Vec::new(),
44241                        double_colon_syntax: true,
44242                        format: None,
44243                        default: None,
44244                        inferred_type: None,
44245                    })));
44246                }
44247            }
44248        }
44249
44250        // Teradata: (FORMAT '...') phrase after a column/expression
44251        if matches!(
44252            self.config.dialect,
44253            Some(crate::dialects::DialectType::Teradata)
44254        ) && self.check(TokenType::LParen)
44255            && self.check_next(TokenType::Format)
44256        {
44257            self.skip(); // consume (
44258            self.skip(); // consume FORMAT
44259            let format = self.expect_string()?;
44260            self.expect(TokenType::RParen)?;
44261            if let Some(expr) = result.take() {
44262                result = Some(Expression::FormatPhrase(Box::new(FormatPhrase {
44263                    this: Box::new(expr),
44264                    format,
44265                })));
44266            }
44267        }
44268
44269        Ok(result)
44270    }
44271
44272    /// parse_column_reference - Parse column reference (field -> Column)
44273    /// Python: this = self._parse_field(); if isinstance(this, exp.Identifier): return exp.Column(this=this)
44274    pub fn parse_column_reference(&mut self) -> Result<Option<Expression>> {
44275        // Parse the field (identifier or literal)
44276        if let Some(field) = self.parse_field()? {
44277            // If it's an identifier, wrap it in a Column expression
44278            match &field {
44279                Expression::Identifier(id) => {
44280                    return Ok(Some(Expression::boxed_column(Column {
44281                        name: id.clone(),
44282                        table: None,
44283                        join_mark: false,
44284                        trailing_comments: Vec::new(),
44285                        span: None,
44286                        inferred_type: None,
44287                    })));
44288                }
44289                // If it's already something else (like a literal), return as-is
44290                _ => return Ok(Some(field)),
44291            }
44292        }
44293        Ok(None)
44294    }
44295
44296    /// parse_command - Parses a generic SQL command
44297    /// Python: _parse_command
44298    /// Used for commands that we don't have specific parsing for
44299    pub fn parse_command(&mut self) -> Result<Option<Expression>> {
44300        // Get the command keyword from the previous token
44301        let command_text = self.previous().text.to_ascii_uppercase();
44302
44303        // Collect remaining tokens as the command expression (until statement end)
44304        // Use (text, token_type) tuples for smart spacing with join_command_tokens
44305        let mut tokens: Vec<(String, TokenType)> = vec![(command_text, TokenType::Var)];
44306        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
44307            let token = self.advance();
44308            // Preserve quotes for quoted identifiers and strings
44309            let text = if token.token_type == TokenType::QuotedIdentifier {
44310                // Re-add the identifier quote characters
44311                // Use backticks as default; this handles MySQL backtick-quoted identifiers
44312                // and double-quoted identifiers for other dialects
44313                let quote_char = if self.config.dialect == Some(crate::dialects::DialectType::MySQL)
44314                    || self.config.dialect == Some(crate::dialects::DialectType::SingleStore)
44315                    || self.config.dialect == Some(crate::dialects::DialectType::Doris)
44316                    || self.config.dialect == Some(crate::dialects::DialectType::StarRocks)
44317                {
44318                    '`'
44319                } else {
44320                    '"'
44321                };
44322                format!("{}{}{}", quote_char, token.text, quote_char)
44323            } else if token.token_type == TokenType::String {
44324                format!("'{}'", token.text)
44325            } else {
44326                token.text.clone()
44327            };
44328            tokens.push((text, token.token_type));
44329        }
44330
44331        Ok(Some(Expression::Command(Box::new(Command {
44332            this: self.join_command_tokens(tokens),
44333        }))))
44334    }
44335
44336    /// parse_commit_or_rollback - Implemented from Python _parse_commit_or_rollback
44337    #[allow(unused_variables, unused_mut)]
44338    pub fn parse_commit_or_rollback(&mut self) -> Result<Option<Expression>> {
44339        if self.match_text_seq(&["TO"]) {
44340            return Ok(Some(Expression::Rollback(Box::new(Rollback {
44341                savepoint: None,
44342                this: None,
44343            }))));
44344        }
44345        if self.match_text_seq(&["SAVEPOINT"]) {
44346            // Matched: SAVEPOINT
44347            return Ok(None);
44348        }
44349        Ok(None)
44350    }
44351
44352    /// parse_composite_key_property - Implemented from Python _parse_composite_key_property
44353    #[allow(unused_variables, unused_mut)]
44354    pub fn parse_composite_key_property(&mut self) -> Result<Option<Expression>> {
44355        if self.match_text_seq(&["KEY"]) {
44356            // Matched: KEY
44357            return Ok(None);
44358        }
44359        Ok(None)
44360    }
44361
44362    /// parse_comprehension - Implemented from Python _parse_comprehension
44363    /// Parses list comprehension: expr FOR var [, position] IN iterator [IF condition]
44364    pub fn parse_comprehension(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
44365        let start_index = self.current;
44366
44367        // Parse expression (column)
44368        let expression = self.parse_column()?;
44369
44370        // Parse optional position (if comma follows)
44371        let position = if self.match_token(TokenType::Comma) {
44372            self.parse_column()?.map(Box::new)
44373        } else {
44374            None
44375        };
44376
44377        // Must have IN keyword
44378        if !self.match_token(TokenType::In) {
44379            // Backtrack
44380            self.current = start_index.saturating_sub(1);
44381            return Ok(None);
44382        }
44383
44384        // Parse iterator
44385        let iterator = self.parse_column()?.map(Box::new);
44386
44387        // Parse optional condition (IF followed by expression)
44388        let condition = if self.match_text_seq(&["IF"]) {
44389            self.parse_disjunction()?.map(Box::new)
44390        } else {
44391            None
44392        };
44393
44394        // Build the comprehension expression
44395        match (this, expression) {
44396            (Some(t), Some(e)) => Ok(Some(Expression::Comprehension(Box::new(Comprehension {
44397                this: Box::new(t),
44398                expression: Box::new(e),
44399                position,
44400                iterator,
44401                condition,
44402            })))),
44403            _ => Ok(None),
44404        }
44405    }
44406
44407    /// parse_compress - Parses COMPRESS column constraint (Teradata)
44408    /// Python: _parse_compress
44409    /// Format: COMPRESS or COMPRESS (value1, value2, ...)
44410    pub fn parse_compress(&mut self) -> Result<Option<Expression>> {
44411        // Check if it's a parenthesized list of values
44412        if self.check(TokenType::LParen) {
44413            // Parse wrapped CSV of bitwise expressions
44414            self.skip(); // consume LParen
44415            let mut expressions = Vec::new();
44416            loop {
44417                if let Some(expr) = self.parse_bitwise()? {
44418                    expressions.push(expr);
44419                } else {
44420                    break;
44421                }
44422                if !self.match_token(TokenType::Comma) {
44423                    break;
44424                }
44425            }
44426            self.expect(TokenType::RParen)?;
44427
44428            // Wrap in a Tuple if multiple values
44429            let this = if expressions.len() == 1 {
44430                Some(Box::new(expressions.into_iter().next().unwrap()))
44431            } else if expressions.is_empty() {
44432                None
44433            } else {
44434                Some(Box::new(Expression::Tuple(Box::new(Tuple { expressions }))))
44435            };
44436
44437            Ok(Some(Expression::CompressColumnConstraint(Box::new(
44438                CompressColumnConstraint { this },
44439            ))))
44440        } else {
44441            // Single value or no value
44442            let this = self.parse_bitwise()?.map(Box::new);
44443            Ok(Some(Expression::CompressColumnConstraint(Box::new(
44444                CompressColumnConstraint { this },
44445            ))))
44446        }
44447    }
44448
44449    /// parse_conjunction - Parses AND expressions
44450    /// Python: _parse_conjunction
44451    /// Delegates to the existing parse_and in the operator precedence chain
44452    pub fn parse_conjunction(&mut self) -> Result<Option<Expression>> {
44453        match self.parse_and() {
44454            Ok(expr) => Ok(Some(expr)),
44455            Err(_) => Ok(None),
44456        }
44457    }
44458
44459    /// parse_connect_with_prior - Parses expression in CONNECT BY context with PRIOR support
44460    /// Python: _parse_connect_with_prior
44461    /// This method temporarily treats PRIOR as a prefix operator while parsing the expression
44462    pub fn parse_connect_with_prior(&mut self) -> Result<Option<Expression>> {
44463        // parse_connect_expression already handles PRIOR as a prefix operator
44464        let connect = self.parse_connect_expression()?;
44465        Ok(Some(connect))
44466    }
44467
44468    /// parse_constraint - Parses named or unnamed constraint
44469    /// Python: _parse_constraint
44470    pub fn parse_constraint(&mut self) -> Result<Option<Expression>> {
44471        // Check for CONSTRAINT keyword (named constraint)
44472        if !self.match_token(TokenType::Constraint) {
44473            // Try to parse an unnamed constraint
44474            return self.parse_unnamed_constraint();
44475        }
44476
44477        // Parse the constraint name
44478        let name = self.parse_id_var()?;
44479        if name.is_none() {
44480            return Ok(None);
44481        }
44482
44483        // Parse the constraint expressions (PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK, etc.)
44484        let expressions = self.parse_unnamed_constraints()?;
44485
44486        Ok(Some(Expression::Constraint(Box::new(Constraint {
44487            this: Box::new(name.unwrap()),
44488            expressions,
44489        }))))
44490    }
44491
44492    /// parse_unnamed_constraints - Parses multiple unnamed constraints
44493    /// Python: _parse_unnamed_constraints
44494    pub fn parse_unnamed_constraints(&mut self) -> Result<Vec<Expression>> {
44495        let mut constraints = Vec::new();
44496
44497        loop {
44498            if let Some(constraint) = self.parse_unnamed_constraint()? {
44499                constraints.push(constraint);
44500            } else {
44501                break;
44502            }
44503        }
44504
44505        Ok(constraints)
44506    }
44507
44508    /// parse_unnamed_constraint - Parses a single unnamed constraint
44509    /// Python: _parse_unnamed_constraint
44510    pub fn parse_unnamed_constraint(&mut self) -> Result<Option<Expression>> {
44511        // Try PRIMARY KEY
44512        if self.match_text_seq(&["PRIMARY", "KEY"]) {
44513            // ClickHouse: PRIMARY KEY expr (without parens) in schema = table-level PK expression
44514            if matches!(
44515                self.config.dialect,
44516                Some(crate::dialects::DialectType::ClickHouse)
44517            ) && !self.check(TokenType::LParen)
44518            {
44519                let expr = self.parse_expression()?;
44520                return Ok(Some(Expression::Raw(Raw {
44521                    sql: format!("PRIMARY KEY {}", expr),
44522                })));
44523            }
44524            return self.parse_primary_key();
44525        }
44526
44527        // Try UNIQUE
44528        if self.match_texts(&["UNIQUE"]) {
44529            return self.parse_unique();
44530        }
44531
44532        // Try FOREIGN KEY
44533        if self.match_text_seq(&["FOREIGN", "KEY"]) {
44534            return self.parse_foreign_key();
44535        }
44536
44537        // Try CHECK
44538        if self.match_texts(&["CHECK"]) {
44539            let expr = self.parse_wrapped()?;
44540            if let Some(check_expr) = expr {
44541                return Ok(Some(Expression::CheckColumnConstraint(Box::new(
44542                    CheckColumnConstraint {
44543                        this: Box::new(check_expr),
44544                        enforced: None,
44545                    },
44546                ))));
44547            }
44548        }
44549
44550        // Try NOT NULL
44551        if self.match_text_seq(&["NOT", "NULL"]) {
44552            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
44553                NotNullColumnConstraint {
44554                    allow_null: None, // NOT NULL means allow_null is not set
44555                },
44556            ))));
44557        }
44558
44559        // Try NULL (allow null)
44560        if self.match_texts(&["NULL"]) {
44561            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
44562                NotNullColumnConstraint {
44563                    allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
44564                        value: true,
44565                    }))),
44566                },
44567            ))));
44568        }
44569
44570        // Try DEFAULT
44571        if self.match_token(TokenType::Default) {
44572            let default_value = self.parse_bitwise()?;
44573            if let Some(val) = default_value {
44574                // TSQL: DEFAULT value FOR column (table-level default constraint)
44575                let for_column = if self.match_token(TokenType::For) {
44576                    Some(self.expect_identifier_with_quoted()?)
44577                } else {
44578                    None
44579                };
44580                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
44581                    DefaultColumnConstraint {
44582                        this: Box::new(val),
44583                        for_column,
44584                    },
44585                ))));
44586            }
44587        }
44588
44589        // Try REFERENCES (inline foreign key)
44590        if self.match_texts(&["REFERENCES"]) {
44591            return self.parse_references();
44592        }
44593
44594        // ClickHouse: INDEX name expr TYPE type_name [GRANULARITY n]
44595        if matches!(
44596            self.config.dialect,
44597            Some(crate::dialects::DialectType::ClickHouse)
44598        ) && self.match_token(TokenType::Index)
44599        {
44600            let name = self.expect_identifier_or_keyword_with_quoted()?;
44601            // Use parse_conjunction to handle comparisons like c0 < (SELECT _table)
44602            let expression = self.parse_conjunction()?.ok_or_else(|| {
44603                self.parse_error("Expected expression in ClickHouse INDEX definition")
44604            })?;
44605            let index_type = if self.match_token(TokenType::Type) {
44606                if let Some(func) = self.parse_function()? {
44607                    Some(Box::new(func))
44608                } else if !self.is_at_end() {
44609                    let type_name = self.advance().text.clone();
44610                    if self.check(TokenType::LParen) {
44611                        self.skip();
44612                        let mut args = Vec::new();
44613                        if !self.check(TokenType::RParen) {
44614                            args.push(self.parse_expression()?);
44615                            while self.match_token(TokenType::Comma) {
44616                                args.push(self.parse_expression()?);
44617                            }
44618                        }
44619                        self.expect(TokenType::RParen)?;
44620                        Some(Box::new(Expression::Function(Box::new(Function::new(
44621                            type_name, args,
44622                        )))))
44623                    } else {
44624                        Some(Box::new(Expression::Identifier(Identifier::new(type_name))))
44625                    }
44626                } else {
44627                    None
44628                }
44629            } else {
44630                None
44631            };
44632            let _granularity = if self.match_identifier("GRANULARITY") {
44633                let _ = self.parse_expression()?;
44634                true
44635            } else {
44636                false
44637            };
44638            // Return as a raw SQL expression preserving the INDEX definition
44639            let mut sql = format!("INDEX {} ", name.name);
44640            if let Some(ref idx_type) = index_type {
44641                sql.push_str(&format!("{} TYPE {} ", expression, idx_type));
44642            }
44643            return Ok(Some(Expression::Raw(Raw {
44644                sql: sql.trim().to_string(),
44645            })));
44646        }
44647
44648        // ClickHouse: PROJECTION name (SELECT ...) or PROJECTION name INDEX expr TYPE type_name
44649        if matches!(
44650            self.config.dialect,
44651            Some(crate::dialects::DialectType::ClickHouse)
44652        ) && self.check_identifier("PROJECTION")
44653        {
44654            self.skip(); // consume PROJECTION
44655            let name = self.expect_identifier_or_keyword_with_quoted()?;
44656            // Parse the projection body - either (SELECT ...) or INDEX expr TYPE type_name
44657            if self.match_token(TokenType::LParen) {
44658                let mut depth = 1i32;
44659                let start = self.current;
44660                while !self.is_at_end() && depth > 0 {
44661                    if self.check(TokenType::LParen) {
44662                        depth += 1;
44663                    }
44664                    if self.check(TokenType::RParen) {
44665                        depth -= 1;
44666                        if depth == 0 {
44667                            break;
44668                        }
44669                    }
44670                    self.skip();
44671                }
44672                let body_sql = self.tokens_to_sql(start, self.current);
44673                self.expect(TokenType::RParen)?;
44674                return Ok(Some(Expression::Raw(Raw {
44675                    sql: format!("PROJECTION {} ({})", name.name, body_sql),
44676                })));
44677            }
44678            // PROJECTION name INDEX expr TYPE type_name
44679            if self.match_token(TokenType::Index) {
44680                let expr = self.parse_bitwise()?.ok_or_else(|| {
44681                    self.parse_error(
44682                        "Expected expression in ClickHouse PROJECTION INDEX definition",
44683                    )
44684                })?;
44685                let type_str = if self.match_token(TokenType::Type) {
44686                    if !self.is_at_end() {
44687                        let t = self.advance().text.clone();
44688                        format!(" TYPE {}", t)
44689                    } else {
44690                        String::new()
44691                    }
44692                } else {
44693                    String::new()
44694                };
44695                return Ok(Some(Expression::Raw(Raw {
44696                    sql: format!("PROJECTION {} INDEX {}{}", name.name, expr, type_str),
44697                })));
44698            }
44699            return Ok(Some(Expression::Raw(Raw {
44700                sql: format!("PROJECTION {}", name.name),
44701            })));
44702        }
44703
44704        Ok(None)
44705    }
44706
44707    /// parse_contains_property - Implemented from Python _parse_contains_property
44708    #[allow(unused_variables, unused_mut)]
44709    pub fn parse_contains_property(&mut self) -> Result<Option<Expression>> {
44710        if self.match_text_seq(&["SQL"]) {
44711            // Matched: SQL
44712            return Ok(None);
44713        }
44714        Ok(None)
44715    }
44716
44717    /// parse_convert - Ported from Python _parse_convert
44718    /// Parses CONVERT function: CONVERT(expr USING charset) or CONVERT(expr, type)
44719    #[allow(unused_variables, unused_mut)]
44720    pub fn parse_convert(&mut self) -> Result<Option<Expression>> {
44721        // Parse the expression to convert
44722        let this = match self.parse_bitwise() {
44723            Ok(Some(expr)) => expr,
44724            Ok(None) => return Ok(None),
44725            Err(e) => return Err(e),
44726        };
44727
44728        // Check for USING charset (CONVERT(x USING utf8))
44729        if self.match_token(TokenType::Using) {
44730            let _ = self.parse_var(); // charset
44731                                      // Return as Cast with charset
44732            return Ok(Some(Expression::Cast(Box::new(Cast {
44733                this,
44734                to: DataType::Char { length: None },
44735                trailing_comments: Vec::new(),
44736                double_colon_syntax: false,
44737                format: None,
44738                default: None,
44739                inferred_type: None,
44740            }))));
44741        }
44742
44743        // Check for comma then type (CONVERT(x, INT))
44744        if self.match_token(TokenType::Comma) {
44745            let data_type = self.parse_data_type()?;
44746            return Ok(Some(Expression::Cast(Box::new(Cast {
44747                this,
44748                to: data_type,
44749                trailing_comments: Vec::new(),
44750                double_colon_syntax: false,
44751                format: None,
44752                default: None,
44753                inferred_type: None,
44754            }))));
44755        }
44756
44757        // No type specified, return as-is wrapped in Cast
44758        Ok(Some(Expression::Cast(Box::new(Cast {
44759            this,
44760            to: DataType::Char { length: None },
44761            trailing_comments: Vec::new(),
44762            double_colon_syntax: false,
44763            format: None,
44764            default: None,
44765            inferred_type: None,
44766        }))))
44767    }
44768
44769    /// parse_copy_parameters - Implemented from Python _parse_copy_parameters
44770    /// parse_copy_parameters - Parses COPY statement parameters
44771    /// Returns a tuple of CopyParameter expressions
44772    pub fn parse_copy_parameters(&mut self) -> Result<Option<Expression>> {
44773        let mut options = Vec::new();
44774
44775        while !self.is_at_end() && !self.check(TokenType::RParen) {
44776            // Parse option name as var
44777            let option = self.parse_var()?;
44778            if option.is_none() {
44779                break;
44780            }
44781
44782            let option_name = match &option {
44783                Some(Expression::Var(v)) => v.this.to_ascii_uppercase(),
44784                Some(Expression::Identifier(id)) => id.name.to_ascii_uppercase(),
44785                _ => String::new(),
44786            };
44787
44788            // Options and values may be separated by whitespace, "=" or "AS"
44789            self.match_token(TokenType::Eq);
44790            self.match_token(TokenType::Alias);
44791
44792            // Parse value based on option type
44793            let (expression, expressions) = if (option_name == "FILE_FORMAT"
44794                || option_name == "FORMAT_OPTIONS")
44795                && self.check(TokenType::LParen)
44796            {
44797                // Parse wrapped options for FILE_FORMAT
44798                let wrapped = self.parse_wrapped_options()?;
44799                let exprs = match wrapped {
44800                    Some(Expression::Tuple(t)) => t.expressions,
44801                    Some(e) => vec![e],
44802                    None => Vec::new(),
44803                };
44804                (None, exprs)
44805            } else if option_name == "FILE_FORMAT" {
44806                // T-SQL external file format case
44807                let field = self.parse_field()?;
44808                (field, Vec::new())
44809            } else if option_name == "FORMAT"
44810                && self.previous().token_type == TokenType::Alias
44811                && self.match_texts(&["AVRO", "JSON"])
44812            {
44813                // FORMAT AS AVRO/JSON
44814                let format_type = self.previous().text.to_ascii_uppercase();
44815                let field = self.parse_field()?;
44816                (
44817                    Some(Expression::Var(Box::new(Var {
44818                        this: format!("FORMAT AS {}", format_type),
44819                    }))),
44820                    field.map_or(Vec::new(), |f| vec![f]),
44821                )
44822            } else {
44823                // Parse unquoted field or bracket
44824                let expr = self
44825                    .parse_unquoted_field()?
44826                    .or_else(|| self.parse_bracket().ok().flatten());
44827                (expr, Vec::new())
44828            };
44829
44830            options.push(Expression::CopyParameter(Box::new(CopyParameter {
44831                name: option_name,
44832                value: expression,
44833                values: expressions,
44834                eq: true,
44835            })));
44836
44837            // Optional comma separator (dialect-specific)
44838            self.match_token(TokenType::Comma);
44839        }
44840
44841        if options.is_empty() {
44842            Ok(None)
44843        } else {
44844            Ok(Some(Expression::Tuple(Box::new(Tuple {
44845                expressions: options,
44846            }))))
44847        }
44848    }
44849
44850    /// parse_copy_property - Implemented from Python _parse_copy_property
44851    #[allow(unused_variables, unused_mut)]
44852    pub fn parse_copy_property(&mut self) -> Result<Option<Expression>> {
44853        if self.match_text_seq(&["GRANTS"]) {
44854            // Matched: GRANTS
44855            return Ok(None);
44856        }
44857        Ok(None)
44858    }
44859
44860    /// parse_create_like - Implemented from Python _parse_create_like
44861    /// Calls: parse_id_var
44862    #[allow(unused_variables, unused_mut)]
44863    pub fn parse_create_like(&mut self) -> Result<Option<Expression>> {
44864        if self.match_texts(&["INCLUDING", "EXCLUDING"]) {
44865            // Matched one of: INCLUDING, EXCLUDING
44866            return Ok(None);
44867        }
44868        Ok(None)
44869    }
44870
44871    /// parse_credentials - Implemented from Python _parse_credentials
44872    #[allow(unused_variables, unused_mut)]
44873    pub fn parse_credentials(&mut self) -> Result<Option<Expression>> {
44874        if self.match_text_seq(&["STORAGE_INTEGRATION", "="]) {
44875            return Ok(Some(Expression::Credentials(Box::new(Credentials {
44876                credentials: Vec::new(),
44877                encryption: None,
44878                storage: None,
44879            }))));
44880        }
44881        if self.match_text_seq(&["CREDENTIALS"]) {
44882            // Matched: CREDENTIALS
44883            return Ok(None);
44884        }
44885        Ok(None)
44886    }
44887
44888    /// parse_csv - Parses comma-separated expressions
44889    /// Python: _parse_csv
44890    /// In Python this takes a parse_method callback, but in Rust we use parse_expression_list
44891    pub fn parse_csv(&mut self) -> Result<Option<Expression>> {
44892        let expressions = self.parse_expression_list()?;
44893        if expressions.is_empty() {
44894            return Ok(None);
44895        }
44896        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
44897    }
44898
44899    /// parse_cte - Implemented from Python _parse_cte
44900    /// Calls: parse_wrapped_id_vars
44901    #[allow(unused_variables, unused_mut)]
44902    pub fn parse_cte(&mut self) -> Result<Option<Expression>> {
44903        if self.match_text_seq(&["USING", "KEY"]) {
44904            return Ok(Some(Expression::Values(Box::new(Values {
44905                expressions: Vec::new(),
44906                alias: None,
44907                column_aliases: Vec::new(),
44908            }))));
44909        }
44910        if self.match_text_seq(&["NOT", "MATERIALIZED"]) {
44911            // Matched: NOT MATERIALIZED
44912            return Ok(None);
44913        }
44914        if self.match_text_seq(&["MATERIALIZED"]) {
44915            // Matched: MATERIALIZED
44916            return Ok(None);
44917        }
44918        Ok(None)
44919    }
44920
44921    /// parse_cube_or_rollup - Ported from Python _parse_cube_or_rollup
44922    /// Parses CUBE(...) or ROLLUP(...) expressions in GROUP BY
44923    #[allow(unused_variables, unused_mut)]
44924    pub fn parse_cube_or_rollup(&mut self) -> Result<Option<Expression>> {
44925        // Check for CUBE or ROLLUP keyword
44926        let is_cube = self.match_texts(&["CUBE"]);
44927        let is_rollup = if !is_cube {
44928            self.match_texts(&["ROLLUP"])
44929        } else {
44930            false
44931        };
44932
44933        if !is_cube && !is_rollup {
44934            return Ok(None);
44935        }
44936
44937        // Parse wrapped expressions
44938        self.expect(TokenType::LParen)?;
44939        let mut expressions = Vec::new();
44940        if !self.check(TokenType::RParen) {
44941            loop {
44942                match self.parse_bitwise() {
44943                    Ok(Some(expr)) => expressions.push(expr),
44944                    Ok(None) => break,
44945                    Err(e) => return Err(e),
44946                }
44947                if !self.match_token(TokenType::Comma) {
44948                    break;
44949                }
44950            }
44951        }
44952        self.expect(TokenType::RParen)?;
44953
44954        if is_cube {
44955            Ok(Some(Expression::Cube(Box::new(Cube { expressions }))))
44956        } else {
44957            Ok(Some(Expression::Rollup(Box::new(Rollup { expressions }))))
44958        }
44959    }
44960
44961    /// parse_data_deletion_property - Implemented from Python _parse_data_deletion_property
44962    /// Calls: parse_column, parse_retention_period
44963    #[allow(unused_variables, unused_mut)]
44964    pub fn parse_data_deletion_property(&mut self) -> Result<Option<Expression>> {
44965        if self.match_text_seq(&["ON"]) {
44966            // Matched: ON
44967            return Ok(None);
44968        }
44969        if self.match_text_seq(&["OFF"]) {
44970            // Matched: OFF
44971            return Ok(None);
44972        }
44973        if self.match_text_seq(&["FILTER_COLUMN", "="]) {
44974            // Matched: FILTER_COLUMN =
44975            return Ok(None);
44976        }
44977        Ok(None)
44978    }
44979
44980    /// parse_datablocksize - Implemented from Python _parse_datablocksize
44981    /// Calls: parse_number
44982    #[allow(unused_variables, unused_mut)]
44983    pub fn parse_datablocksize(&mut self) -> Result<Option<Expression>> {
44984        if self.match_texts(&["BYTES", "KBYTES", "KILOBYTES"]) {
44985            // Matched one of: BYTES, KBYTES, KILOBYTES
44986            return Ok(None);
44987        }
44988        Ok(None)
44989    }
44990
44991    /// parse_dcolon - Delegates to parse_types
44992    #[allow(unused_variables, unused_mut)]
44993    pub fn parse_dcolon(&mut self) -> Result<Option<Expression>> {
44994        self.parse_types()
44995    }
44996
44997    /// parse_ddl_select - Ported from Python _parse_ddl_select
44998    /// Parses a SELECT statement in DDL context (CREATE TABLE AS SELECT, INSERT INTO ... SELECT)
44999    #[allow(unused_variables, unused_mut)]
45000    pub fn parse_ddl_select(&mut self) -> Result<Option<Expression>> {
45001        // Parse a nested SELECT statement
45002        let select = self.parse_select_query()?;
45003
45004        if select.is_none() {
45005            return Ok(None);
45006        }
45007
45008        // Apply set operations (UNION, INTERSECT, EXCEPT)
45009        let with_set_ops = self.parse_set_operations_with_expr(select)?;
45010
45011        // Return the result (query modifiers would be applied by parse_select_query already)
45012        Ok(with_set_ops)
45013    }
45014
45015    /// parse_for_in - BigQuery procedural FOR...IN...DO loop
45016    /// Python: BigQuery._parse_for_in
45017    /// Format: FOR variable IN (query) DO statement(s) END FOR
45018    /// Example: FOR record IN (SELECT * FROM t) DO SELECT record.col
45019    pub fn parse_for_in(&mut self) -> Result<Expression> {
45020        // Parse: variable IN (query)
45021        // This is handled by parse_range which produces an In expression
45022        let this = self
45023            .parse_range()?
45024            .ok_or_else(|| self.parse_error("Expected expression after FOR"))?;
45025
45026        // Match DO keyword
45027        self.match_text_seq(&["DO"]);
45028
45029        // Parse the body statement
45030        let expression = self.parse_statement()?;
45031
45032        Ok(Expression::ForIn(Box::new(ForIn {
45033            this: Box::new(this),
45034            expression: Box::new(expression),
45035        })))
45036    }
45037
45038    /// parse_declare - Parses DECLARE statement
45039    /// Python: _parse_declare
45040    /// Format: DECLARE var1 type [DEFAULT expr], var2 type [DEFAULT expr], ...
45041    pub fn parse_declare(&mut self) -> Result<Option<Expression>> {
45042        // Check for OR REPLACE (Spark/Databricks)
45043        let replace = self.match_text_seq(&["OR", "REPLACE"]);
45044
45045        // Try to parse comma-separated declare items
45046        let mut expressions = Vec::new();
45047
45048        // BigQuery multi-variable DECLARE: DECLARE X, Y, Z INT64 [DEFAULT expr]
45049        // Detect by looking ahead: if we see identifier, comma, identifier pattern
45050        // before a data type keyword, collect all names then parse type once.
45051        let saved = self.current;
45052        let mut multi_names: Vec<Expression> = Vec::new();
45053        if let Some(first_var) = self.parse_id_var()? {
45054            // Check if next is a comma (BigQuery multi-var syntax)
45055            if self.check(TokenType::Comma) && !self.check_identifier("CURSOR") {
45056                // Speculatively collect comma-separated identifiers
45057                multi_names.push(first_var);
45058                while self.match_token(TokenType::Comma) {
45059                    if let Some(next_var) = self.parse_id_var()? {
45060                        multi_names.push(next_var);
45061                    } else {
45062                        break;
45063                    }
45064                }
45065                // Now check if we're at a data type (not comma, not @, not semicolon)
45066                // If so, this is BigQuery multi-var syntax
45067                if multi_names.len() > 1 && !self.is_at_end() && !self.check(TokenType::Semicolon) {
45068                    let data_type = self.parse_data_type()?;
45069                    let kind_str = self.data_type_to_sql(&data_type);
45070                    let default = if self.match_token(TokenType::Default)
45071                        || self.match_token(TokenType::Eq)
45072                    {
45073                        Some(Box::new(self.parse_expression()?))
45074                    } else {
45075                        None
45076                    };
45077                    let first_name = multi_names.remove(0);
45078                    expressions.push(Expression::DeclareItem(Box::new(DeclareItem {
45079                        this: Box::new(first_name),
45080                        kind: Some(kind_str),
45081                        default,
45082                        has_as: false,
45083                        additional_names: multi_names,
45084                    })));
45085                    return Ok(Some(Expression::Declare(Box::new(Declare {
45086                        expressions,
45087                        replace,
45088                    }))));
45089                }
45090            }
45091        }
45092        // Reset and parse normally
45093        self.current = saved;
45094
45095        loop {
45096            if let Some(item) = self.parse_declareitem()? {
45097                expressions.push(item);
45098            } else {
45099                break;
45100            }
45101            if !self.match_token(TokenType::Comma) {
45102                break;
45103            }
45104        }
45105
45106        // If we successfully parsed at least one item, return the Declare
45107        if !expressions.is_empty() {
45108            return Ok(Some(Expression::Declare(Box::new(Declare {
45109                expressions,
45110                replace,
45111            }))));
45112        }
45113
45114        Ok(None)
45115    }
45116
45117    /// parse_declareitem - Parse a DECLARE item (variable declaration)
45118    /// TSQL format: @var AS type [= expr] or @var type [= expr]
45119    /// Also handles: DECLARE name CURSOR FOR SELECT ...
45120    /// Also handles: DECLARE @var TABLE (col_defs)
45121    #[allow(unused_variables, unused_mut)]
45122    pub fn parse_declareitem(&mut self) -> Result<Option<Expression>> {
45123        // Consume optional VAR or VARIABLE keyword (Spark/Databricks)
45124        if self.check_identifier("VAR") || self.check_identifier("VARIABLE") {
45125            self.skip();
45126        }
45127
45128        // Parse the variable name (starts with @ or is a cursor name)
45129        let var = if let Some(v) = self.parse_id_var()? {
45130            v
45131        } else {
45132            return Ok(None);
45133        };
45134
45135        // Check for CURSOR FOR syntax: DECLARE name CURSOR FOR SELECT ...
45136        if self.check_identifier("CURSOR") {
45137            self.skip(); // consume CURSOR
45138                         // Parse optional cursor options before FOR (e.g., SCROLL, INSENSITIVE, etc.)
45139                         // For now just look for FOR
45140            if self.match_token(TokenType::For) {
45141                // Capture the remaining tokens as the cursor query using tokens_to_sql for proper spacing
45142                let start = self.current;
45143                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
45144                    self.skip();
45145                }
45146                let query_str = self.tokens_to_sql_uppercased(start, self.current);
45147                let kind_str = format!("CURSOR FOR {}", query_str);
45148                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45149                    this: Box::new(var),
45150                    kind: Some(kind_str),
45151                    default: None,
45152                    has_as: false,
45153                    additional_names: Vec::new(),
45154                }))));
45155            } else {
45156                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45157                    this: Box::new(var),
45158                    kind: Some("CURSOR".to_string()),
45159                    default: None,
45160                    has_as: false,
45161                    additional_names: Vec::new(),
45162                }))));
45163            }
45164        }
45165
45166        // Parse optional AS keyword
45167        let has_as = self.match_token(TokenType::As);
45168
45169        // Check for TABLE type with column definitions
45170        if self.check(TokenType::Table) {
45171            self.skip(); // consume TABLE
45172            if self.match_token(TokenType::LParen) {
45173                // Parse the TABLE column definitions using tokens_to_sql for proper spacing
45174                let start = self.current;
45175                let mut depth = 1;
45176                while depth > 0 && !self.is_at_end() {
45177                    if self.check(TokenType::LParen) {
45178                        depth += 1;
45179                    }
45180                    if self.check(TokenType::RParen) {
45181                        depth -= 1;
45182                        if depth == 0 {
45183                            break;
45184                        }
45185                    }
45186                    self.skip();
45187                }
45188                let col_defs_str = self.tokens_to_sql_uppercased(start, self.current);
45189                self.expect(TokenType::RParen)?;
45190                let kind_str = format!("TABLE ({})", col_defs_str);
45191                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45192                    this: Box::new(var),
45193                    kind: Some(kind_str),
45194                    default: None,
45195                    has_as,
45196                    additional_names: Vec::new(),
45197                }))));
45198            } else {
45199                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45200                    this: Box::new(var),
45201                    kind: Some("TABLE".to_string()),
45202                    default: None,
45203                    has_as,
45204                    additional_names: Vec::new(),
45205                }))));
45206            }
45207        }
45208
45209        // Check if next token is = or DEFAULT (no type, just default value)
45210        // or if at end of statement (no type, no default)
45211        let kind_str = if self.check(TokenType::Eq)
45212            || self.check(TokenType::Default)
45213            || self.is_at_end()
45214            || self.check(TokenType::Semicolon)
45215            || self.check(TokenType::Comma)
45216        {
45217            // No type specified
45218            None
45219        } else {
45220            // Parse the data type
45221            let data_type = self.parse_data_type()?;
45222            Some(self.data_type_to_sql(&data_type))
45223        };
45224
45225        // Parse optional DEFAULT value or = value (TSQL uses =)
45226        let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq) {
45227            Some(Box::new(self.parse_expression()?))
45228        } else {
45229            None
45230        };
45231
45232        Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45233            this: Box::new(var),
45234            kind: kind_str,
45235            default,
45236            has_as,
45237            additional_names: Vec::new(),
45238        }))))
45239    }
45240
45241    /// Convert a DataType to its SQL string representation
45242    fn data_type_to_sql(&self, dt: &DataType) -> String {
45243        match dt {
45244            DataType::Boolean => "BOOLEAN".to_string(),
45245            DataType::TinyInt { length } => {
45246                if let Some(n) = length {
45247                    format!("TINYINT({})", n)
45248                } else {
45249                    "TINYINT".to_string()
45250                }
45251            }
45252            DataType::SmallInt { length } => {
45253                if let Some(n) = length {
45254                    format!("SMALLINT({})", n)
45255                } else {
45256                    "SMALLINT".to_string()
45257                }
45258            }
45259            DataType::Int {
45260                length,
45261                integer_spelling,
45262            } => {
45263                if let Some(n) = length {
45264                    if *integer_spelling {
45265                        format!("INTEGER({})", n)
45266                    } else {
45267                        format!("INT({})", n)
45268                    }
45269                } else if *integer_spelling {
45270                    "INTEGER".to_string()
45271                } else {
45272                    "INT".to_string()
45273                }
45274            }
45275            DataType::BigInt { length } => {
45276                if let Some(n) = length {
45277                    format!("BIGINT({})", n)
45278                } else {
45279                    "BIGINT".to_string()
45280                }
45281            }
45282            DataType::Float {
45283                precision, scale, ..
45284            } => match (precision, scale) {
45285                (Some(p), Some(s)) => format!("FLOAT({}, {})", p, s),
45286                (Some(p), None) => format!("FLOAT({})", p),
45287                _ => "FLOAT".to_string(),
45288            },
45289            DataType::Double { precision, scale } => match (precision, scale) {
45290                (Some(p), Some(s)) => format!("DOUBLE({}, {})", p, s),
45291                (Some(p), None) => format!("DOUBLE({})", p),
45292                _ => "DOUBLE".to_string(),
45293            },
45294            DataType::Decimal { precision, scale } => match (precision, scale) {
45295                (Some(p), Some(s)) => format!("DECIMAL({}, {})", p, s),
45296                (Some(p), None) => format!("DECIMAL({})", p),
45297                _ => "DECIMAL".to_string(),
45298            },
45299            DataType::Char { length } => {
45300                if let Some(n) = length {
45301                    format!("CHAR({})", n)
45302                } else {
45303                    "CHAR".to_string()
45304                }
45305            }
45306            DataType::VarChar { length, .. } => {
45307                if let Some(n) = length {
45308                    format!("VARCHAR({})", n)
45309                } else {
45310                    "VARCHAR".to_string()
45311                }
45312            }
45313            DataType::Text => "TEXT".to_string(),
45314            DataType::Date => "DATE".to_string(),
45315            DataType::Time { precision, .. } => {
45316                if let Some(p) = precision {
45317                    format!("TIME({})", p)
45318                } else {
45319                    "TIME".to_string()
45320                }
45321            }
45322            DataType::Timestamp { precision, .. } => {
45323                if let Some(p) = precision {
45324                    format!("TIMESTAMP({})", p)
45325                } else {
45326                    "TIMESTAMP".to_string()
45327                }
45328            }
45329            DataType::Binary { length } => {
45330                if let Some(n) = length {
45331                    format!("BINARY({})", n)
45332                } else {
45333                    "BINARY".to_string()
45334                }
45335            }
45336            DataType::VarBinary { length } => {
45337                if let Some(n) = length {
45338                    format!("VARBINARY({})", n)
45339                } else {
45340                    "VARBINARY".to_string()
45341                }
45342            }
45343            DataType::Blob => "BLOB".to_string(),
45344            DataType::String { length: Some(n) } => format!("STRING({})", n),
45345            DataType::String { length: None } => "STRING".to_string(),
45346            DataType::Json => "JSON".to_string(),
45347            DataType::Uuid => "UUID".to_string(),
45348            DataType::Custom { name } => name.clone(), // Custom types (INT64, FLOAT64, etc.)
45349            _ => format!("{:?}", dt),                  // Fallback for unknown types
45350        }
45351    }
45352
45353    /// parse_decode - Ported from Python _parse_decode
45354    /// Parses Oracle-style DECODE or simple DECODE function
45355    /// If 3+ args: Oracle DECODE(expr, search1, result1, ..., default)
45356    /// If 2 args: character set decode (expr, charset)
45357    #[allow(unused_variables, unused_mut)]
45358    pub fn parse_decode(&mut self) -> Result<Option<Expression>> {
45359        // Parse comma-separated arguments
45360        let mut args: Vec<Expression> = Vec::new();
45361        loop {
45362            match self.parse_expression() {
45363                Ok(expr) => args.push(expr),
45364                Err(_) => break,
45365            }
45366            if !self.match_token(TokenType::Comma) {
45367                break;
45368            }
45369        }
45370
45371        if args.len() < 3 {
45372            // Simple decode with charset
45373            return Ok(Some(Expression::DecodeCase(Box::new(DecodeCase {
45374                expressions: args,
45375            }))));
45376        }
45377
45378        // Oracle DECODE: first arg is the expression being compared
45379        // Remaining args are search/result pairs, with optional default at end
45380        Ok(Some(Expression::DecodeCase(Box::new(DecodeCase {
45381            expressions: args,
45382        }))))
45383    }
45384
45385    /// parse_definer - MySQL DEFINER property
45386    /// Parses: DEFINER = user@host
45387    #[allow(unused_variables, unused_mut)]
45388    pub fn parse_definer(&mut self) -> Result<Option<Expression>> {
45389        // Optionally consume = sign
45390        self.match_token(TokenType::Eq);
45391
45392        // Parse the user part
45393        let user = self.parse_id_var()?;
45394        if user.is_none() {
45395            return Ok(None);
45396        }
45397
45398        // Expect @ symbol
45399        if !self.match_token(TokenType::DAt) {
45400            return Ok(None);
45401        }
45402
45403        // Parse the host part (can be identifier or % wildcard)
45404        let host = if let Some(id) = self.parse_id_var()? {
45405            id
45406        } else if self.match_token(TokenType::Mod) {
45407            // % wildcard for any host
45408            Expression::Identifier(Identifier::new(self.previous().text.clone()))
45409        } else {
45410            return Ok(None);
45411        };
45412
45413        // Combine user@host into a string
45414        let user_str = match &user {
45415            Some(Expression::Identifier(id)) => id.name.clone(),
45416            _ => "".to_string(),
45417        };
45418        let host_str = match &host {
45419            Expression::Identifier(id) => id.name.clone(),
45420            _ => "".to_string(),
45421        };
45422
45423        let definer_str = format!("{}@{}", user_str, host_str);
45424
45425        Ok(Some(Expression::DefinerProperty(Box::new(
45426            DefinerProperty {
45427                this: Box::new(Expression::Literal(Box::new(Literal::String(definer_str)))),
45428            },
45429        ))))
45430    }
45431
45432    /// parse_derived_table_values - Implemented from Python _parse_derived_table_values
45433    #[allow(unused_variables, unused_mut)]
45434    pub fn parse_derived_table_values(&mut self) -> Result<Option<Expression>> {
45435        if self.match_text_seq(&["VALUES"]) {
45436            return Ok(Some(Expression::Values(Box::new(Values {
45437                expressions: Vec::new(),
45438                alias: None,
45439                column_aliases: Vec::new(),
45440            }))));
45441        }
45442        if self.match_text_seq(&["FORMAT", "VALUES"]) {
45443            // Matched: FORMAT VALUES
45444            return Ok(None);
45445        }
45446        Ok(None)
45447    }
45448
45449    /// parse_dict_property - ClickHouse dictionary property
45450    /// Parses: property_name(kind(key1 value1, key2 value2, ...))
45451    /// property_name should be the already matched property keyword (LAYOUT, SOURCE, etc.)
45452    #[allow(unused_variables, unused_mut)]
45453    pub fn parse_dict_property(&mut self, property_name: &str) -> Result<Option<Expression>> {
45454        // Expect opening paren
45455        if !self.match_token(TokenType::LParen) {
45456            return Ok(None);
45457        }
45458
45459        // Parse the kind (e.g., HASHED, FLAT, CLICKHOUSE, CACHE, etc.)
45460        // Accept Var, Identifier, or keyword tokens as the kind name
45461        let kind_str = if self.is_identifier_token() || self.check_keyword() {
45462            self.advance().text.clone()
45463        } else {
45464            String::new()
45465        };
45466        if kind_str.is_empty() {
45467            return Err(self.parse_error("Expected dictionary property kind"));
45468        }
45469
45470        // Parse optional settings in nested parens
45471        let settings = if self.match_token(TokenType::LParen) {
45472            let mut setting_pairs = Vec::new();
45473            loop {
45474                let key = if let Some(k) = self.parse_id_var()? {
45475                    Some(k)
45476                } else if self.is_safe_keyword_as_identifier() || self.check_keyword() {
45477                    let name = self.advance().text.clone();
45478                    Some(Expression::Identifier(Identifier::new(name)))
45479                } else if !self.check(TokenType::RParen) && !self.check(TokenType::Comma) {
45480                    let name = self.advance().text.clone();
45481                    Some(Expression::Identifier(Identifier::new(name)))
45482                } else {
45483                    None
45484                };
45485                // ClickHouse: STRUCTURE (...) contains column defs without commas — consume balanced parens
45486                let is_structure = key.as_ref().map_or(false, |k| {
45487                    matches!(k, Expression::Identifier(id) if id.name.eq_ignore_ascii_case("STRUCTURE"))
45488                });
45489                let value = if is_structure && self.check(TokenType::LParen) {
45490                    let mut raw = String::new();
45491                    let mut depth = 0i32;
45492                    while !self.is_at_end() {
45493                        let tok = self.advance();
45494                        match tok.token_type {
45495                            TokenType::LParen => {
45496                                depth += 1;
45497                                raw.push('(');
45498                            }
45499                            TokenType::RParen => {
45500                                depth -= 1;
45501                                if depth == 0 {
45502                                    raw.push(')');
45503                                    break;
45504                                }
45505                                raw.push(')');
45506                            }
45507                            _ => {
45508                                if !raw.is_empty() && !raw.ends_with('(') {
45509                                    raw.push(' ');
45510                                }
45511                                raw.push_str(&tok.text);
45512                            }
45513                        }
45514                    }
45515                    Some(Expression::Var(Box::new(Var { this: raw })))
45516                } else {
45517                    self.parse_primary_or_var()?
45518                };
45519                if key.is_none() && value.is_none() {
45520                    break;
45521                }
45522                if let (Some(k), Some(v)) = (key, value) {
45523                    // Store as a tuple-like expression
45524                    setting_pairs.push(Expression::Tuple(Box::new(Tuple {
45525                        expressions: vec![k, v],
45526                    })));
45527                }
45528                // ClickHouse dict properties are space-separated, not comma-separated
45529                // e.g. SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() DB 'test'))
45530                // Accept optional comma but don't require it
45531                self.match_token(TokenType::Comma);
45532                // Break if we see RParen (end of settings)
45533                if self.check(TokenType::RParen) {
45534                    break;
45535                }
45536            }
45537            self.expect(TokenType::RParen)?;
45538            if !setting_pairs.is_empty() {
45539                Some(Box::new(Expression::Tuple(Box::new(Tuple {
45540                    expressions: setting_pairs,
45541                }))))
45542            } else {
45543                None
45544            }
45545        } else {
45546            None
45547        };
45548
45549        self.expect(TokenType::RParen)?;
45550
45551        Ok(Some(Expression::DictProperty(Box::new(DictProperty {
45552            this: Box::new(Expression::Identifier(Identifier::new(
45553                property_name.to_string(),
45554            ))),
45555            kind: kind_str,
45556            settings,
45557        }))))
45558    }
45559
45560    /// parse_dict_range - Implemented from Python _parse_dict_range
45561    /// Parses dictionary range specification: (MIN min_val MAX max_val) or (max_val)
45562    pub fn parse_dict_range(&mut self, property_name: &str) -> Result<Option<Expression>> {
45563        // Expect opening paren
45564        self.expect(TokenType::LParen)?;
45565
45566        // Prefer id/var first for dictionary bounds to avoid function-keyword ambiguity
45567        // such as `MIN discount_start_date MAX discount_end_date`.
45568        let parse_bound = |parser: &mut Parser| -> Result<Option<Expression>> {
45569            // Handle negative numbers: -1, -100, etc.
45570            if parser.check(TokenType::Dash)
45571                && parser
45572                    .peek_nth(1)
45573                    .is_some_and(|t| t.token_type == TokenType::Number)
45574            {
45575                parser.advance(); // consume -
45576                let num = parser.advance().text.clone();
45577                return Ok(Some(Expression::Literal(Box::new(Literal::Number(
45578                    format!("-{}", num),
45579                )))));
45580            }
45581            if let Some(id) = parser.parse_id_var()? {
45582                return Ok(Some(id));
45583            }
45584            parser.parse_primary_or_var()
45585        };
45586
45587        let (min_val, max_val) = if self.peek().text.eq_ignore_ascii_case("MIN") {
45588            self.skip(); // consume MIN
45589            let min = parse_bound(self)?;
45590            if self.peek().text.eq_ignore_ascii_case("MAX") {
45591                self.skip(); // consume MAX
45592            }
45593            let max = parse_bound(self)?;
45594            (min, max)
45595        } else {
45596            let max = parse_bound(self)?;
45597            let min = Some(Expression::Literal(Box::new(Literal::Number(
45598                "0".to_string(),
45599            ))));
45600            (min, max)
45601        };
45602
45603        // Match closing paren
45604        self.expect(TokenType::RParen)?;
45605
45606        Ok(Some(Expression::DictRange(Box::new(DictRange {
45607            this: Box::new(Expression::Var(Box::new(Var {
45608                this: property_name.to_string(),
45609            }))),
45610            min: min_val.map(Box::new),
45611            max: max_val.map(Box::new),
45612        }))))
45613    }
45614
45615    /// parse_disjunction - Parses OR expressions
45616    /// Python: _parse_disjunction
45617    /// Delegates to the existing parse_or in the operator precedence chain
45618    pub fn parse_disjunction(&mut self) -> Result<Option<Expression>> {
45619        match self.parse_or() {
45620            Ok(expr) => Ok(Some(expr)),
45621            Err(_) => Ok(None),
45622        }
45623    }
45624
45625    /// parse_distkey - Redshift DISTKEY property for distribution key
45626    /// Parses: DISTKEY(column_name)
45627    #[allow(unused_variables, unused_mut)]
45628    pub fn parse_distkey(&mut self) -> Result<Option<Expression>> {
45629        // Parse wrapped column identifier (in parentheses)
45630        if !self.match_token(TokenType::LParen) {
45631            return Ok(None);
45632        }
45633
45634        let column = self.parse_id_var()?;
45635        if column.is_none() {
45636            return Ok(None);
45637        }
45638
45639        self.match_token(TokenType::RParen);
45640
45641        Ok(Some(Expression::DistKeyProperty(Box::new(
45642            DistKeyProperty {
45643                this: Box::new(column.unwrap()),
45644            },
45645        ))))
45646    }
45647
45648    /// parse_distributed_property - Implemented from Python _parse_distributed_property
45649    #[allow(unused_variables, unused_mut)]
45650    /// parse_distributed_property - Parses DISTRIBUTED BY property
45651    /// Python: parser.py:2462-2481
45652    pub fn parse_distributed_property(&mut self) -> Result<Option<Expression>> {
45653        let mut kind = "HASH".to_string();
45654        let mut expressions = Vec::new();
45655
45656        if self.match_text_seq(&["BY", "HASH"]) {
45657            // Parse column list: (col1, col2, ...)
45658            if let Some(wrapped) = self.parse_wrapped_id_vars()? {
45659                if let Expression::Tuple(t) = wrapped {
45660                    expressions = t.expressions;
45661                }
45662            }
45663        } else if self.match_text_seq(&["BY", "RANDOM"]) {
45664            kind = "RANDOM".to_string();
45665        } else {
45666            return Ok(None);
45667        }
45668
45669        // Parse optional BUCKETS
45670        let buckets = if self.match_text_seq(&["BUCKETS"]) {
45671            if !self.match_text_seq(&["AUTO"]) {
45672                self.parse_number()?
45673            } else {
45674                None
45675            }
45676        } else {
45677            None
45678        };
45679
45680        // Parse optional ORDER BY
45681        let order = self.parse_order()?;
45682
45683        Ok(Some(Expression::DistributedByProperty(Box::new(
45684            DistributedByProperty {
45685                expressions,
45686                kind,
45687                buckets: buckets.map(Box::new),
45688                order: order.map(Box::new),
45689            },
45690        ))))
45691    }
45692
45693    /// Parse DROP COLUMN in ALTER TABLE
45694    /// Note: Main ALTER TABLE DROP COLUMN logic is in parse_alter_table -> AlterTableAction::DropColumn
45695    pub fn parse_drop_column(&mut self) -> Result<Option<Expression>> {
45696        // Optionally match COLUMN keyword
45697        self.match_token(TokenType::Column);
45698
45699        // Parse IF EXISTS
45700        let _if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
45701
45702        // Parse the column identifier
45703        if let Some(column) = self.parse_identifier()? {
45704            // Check for CASCADE
45705            let _cascade = self.match_text_seq(&["CASCADE"]);
45706            // Return the column as an identifier (the caller handles the drop semantics)
45707            Ok(Some(column))
45708        } else {
45709            Ok(None)
45710        }
45711    }
45712
45713    /// Parse DROP PARTITION in ALTER TABLE
45714    /// Note: Main ALTER TABLE DROP PARTITION logic is in parse_alter_table -> AlterTableAction::DropPartition
45715    pub fn parse_drop_partition(&mut self) -> Result<Option<Expression>> {
45716        self.parse_drop_partition_with_exists(false)
45717    }
45718
45719    /// Parse DROP PARTITION with exists flag
45720    pub fn parse_drop_partition_with_exists(&mut self, exists: bool) -> Result<Option<Expression>> {
45721        // Parse one or more partitions
45722        let mut partitions = Vec::new();
45723
45724        loop {
45725            // Parse PARTITION (key = value, ...)
45726            if self.match_token(TokenType::Partition) {
45727                if self.match_token(TokenType::LParen) {
45728                    // Parse partition expressions
45729                    let mut exprs = Vec::new();
45730                    loop {
45731                        let expr = self.parse_expression()?;
45732                        exprs.push(expr);
45733                        if !self.match_token(TokenType::Comma) {
45734                            break;
45735                        }
45736                    }
45737                    self.match_token(TokenType::RParen);
45738                    partitions.push(Expression::Tuple(Box::new(Tuple { expressions: exprs })));
45739                }
45740            } else {
45741                break;
45742            }
45743
45744            if !self.match_token(TokenType::Comma) {
45745                break;
45746            }
45747        }
45748
45749        if partitions.is_empty() {
45750            Ok(None)
45751        } else {
45752            Ok(Some(Expression::DropPartition(Box::new(DropPartition {
45753                expressions: partitions,
45754                exists,
45755            }))))
45756        }
45757    }
45758
45759    /// parse_equality - Parses comparison/equality expressions (= <> < > <= >=)
45760    /// Python: _parse_equality
45761    /// Delegates to the existing parse_comparison in the operator precedence chain
45762    pub fn parse_equality(&mut self) -> Result<Option<Expression>> {
45763        match self.parse_comparison() {
45764            Ok(expr) => Ok(Some(expr)),
45765            Err(_) => Ok(None),
45766        }
45767    }
45768
45769    /// parse_escape - Parses ESCAPE clause for LIKE patterns
45770    /// Python: _parse_escape
45771    /// Returns the escape character/expression if ESCAPE keyword is found
45772    pub fn parse_escape(&mut self) -> Result<Option<Expression>> {
45773        if !self.match_token(TokenType::Escape) {
45774            return Ok(None);
45775        }
45776
45777        // Parse escape character (usually a string like '\')
45778        if let Some(escape_char) = self.parse_string()? {
45779            return Ok(Some(escape_char));
45780        }
45781
45782        // Or parse NULL
45783        if let Some(null_expr) = self.parse_null()? {
45784            return Ok(Some(null_expr));
45785        }
45786
45787        Ok(None)
45788    }
45789
45790    /// parse_exists - Implemented from Python _parse_exists
45791    #[allow(unused_variables, unused_mut)]
45792    pub fn parse_exists(&mut self) -> Result<Option<Expression>> {
45793        if self.match_text_seq(&["IF"]) {
45794            // Matched: IF
45795            return Ok(None);
45796        }
45797        Ok(None)
45798    }
45799
45800    /// parse_exponent - Parses exponent/power expressions
45801    /// Python: _parse_exponent
45802    /// In most dialects, EXPONENT is empty, so this delegates to parse_unary
45803    pub fn parse_exponent(&mut self) -> Result<Option<Expression>> {
45804        match self.parse_unary() {
45805            Ok(expr) => Ok(Some(expr)),
45806            Err(_) => Ok(None),
45807        }
45808    }
45809
45810    /// parse_expressions - Parse comma-separated expressions
45811    /// Returns a Tuple containing all expressions, or None if empty
45812    #[allow(unused_variables, unused_mut)]
45813    pub fn parse_expressions(&mut self) -> Result<Option<Expression>> {
45814        let expressions = self.parse_expression_list()?;
45815        if expressions.is_empty() {
45816            return Ok(None);
45817        }
45818        if expressions.len() == 1 {
45819            return Ok(expressions.into_iter().next());
45820        }
45821        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
45822    }
45823
45824    /// parse_extract - Ported from Python _parse_extract
45825    /// Parses EXTRACT(field FROM expression) function
45826    #[allow(unused_variables, unused_mut)]
45827    pub fn parse_extract(&mut self) -> Result<Option<Expression>> {
45828        // Parse the field (YEAR, MONTH, DAY, HOUR, etc.)
45829        let field_name = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
45830            let token = self.advance();
45831            token.text.to_ascii_uppercase()
45832        } else {
45833            return Ok(None);
45834        };
45835
45836        // Convert field name to DateTimeField
45837        let field = match field_name.as_str() {
45838            "YEAR" => DateTimeField::Year,
45839            "MONTH" => DateTimeField::Month,
45840            "DAY" => DateTimeField::Day,
45841            "HOUR" => DateTimeField::Hour,
45842            "MINUTE" => DateTimeField::Minute,
45843            "SECOND" => DateTimeField::Second,
45844            "MILLISECOND" | "MILLISECONDS" | "MS" => DateTimeField::Millisecond,
45845            "MICROSECOND" | "MICROSECONDS" | "US" => DateTimeField::Microsecond,
45846            "DOW" | "DAYOFWEEK" => DateTimeField::DayOfWeek,
45847            "DOY" | "DAYOFYEAR" => DateTimeField::DayOfYear,
45848            "WEEK" => DateTimeField::Week,
45849            "QUARTER" => DateTimeField::Quarter,
45850            "EPOCH" => DateTimeField::Epoch,
45851            "TIMEZONE" => DateTimeField::Timezone,
45852            "TIMEZONE_HOUR" => DateTimeField::TimezoneHour,
45853            "TIMEZONE_MINUTE" => DateTimeField::TimezoneMinute,
45854            "DATE" => DateTimeField::Date,
45855            "TIME" => DateTimeField::Time,
45856            other => DateTimeField::Custom(other.to_string()),
45857        };
45858
45859        // Expect FROM or comma
45860        if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
45861            return Err(self.parse_error("Expected FROM or comma after EXTRACT field"));
45862        }
45863
45864        // Parse the expression to extract from
45865        let expression = self.parse_bitwise()?;
45866        let this = match expression {
45867            Some(expr) => self.try_clickhouse_func_arg_alias(expr),
45868            None => return Err(self.parse_error("Expected expression after FROM in EXTRACT")),
45869        };
45870
45871        Ok(Some(Expression::Extract(Box::new(ExtractFunc {
45872            this,
45873            field,
45874        }))))
45875    }
45876
45877    /// parse_factor - Parses multiplication/division expressions (* / % operators)
45878    /// Python: _parse_factor
45879    /// Delegates to the existing parse_multiplication in the operator precedence chain
45880    pub fn parse_factor(&mut self) -> Result<Option<Expression>> {
45881        // Delegate to the existing multiplication parsing
45882        match self.parse_multiplication() {
45883            Ok(expr) => Ok(Some(expr)),
45884            Err(_) => Ok(None),
45885        }
45886    }
45887
45888    /// parse_fallback - Implemented from Python _parse_fallback
45889    #[allow(unused_variables, unused_mut)]
45890    pub fn parse_fallback(&mut self) -> Result<Option<Expression>> {
45891        if self.match_text_seq(&["PROTECTION"]) {
45892            return Ok(Some(Expression::FallbackProperty(Box::new(
45893                FallbackProperty {
45894                    no: None,
45895                    protection: None,
45896                },
45897            ))));
45898        }
45899        Ok(None)
45900    }
45901
45902    /// parse_field - Parse a field (column name, literal, or expression)
45903    /// Python: field = self._parse_primary() or self._parse_function() or self._parse_id_var()
45904    pub fn parse_field(&mut self) -> Result<Option<Expression>> {
45905        // Try parsing literals first
45906        if let Some(expr) = self.parse_string()? {
45907            return Ok(Some(expr));
45908        }
45909        if let Some(expr) = self.parse_number()? {
45910            return Ok(Some(expr));
45911        }
45912        if let Some(expr) = self.parse_boolean()? {
45913            return Ok(Some(expr));
45914        }
45915        if let Some(expr) = self.parse_null()? {
45916            return Ok(Some(expr));
45917        }
45918        if let Some(expr) = self.parse_star()? {
45919            return Ok(Some(expr));
45920        }
45921        // Try parsing identifier
45922        if let Some(expr) = self.parse_identifier()? {
45923            return Ok(Some(expr));
45924        }
45925        // Try parsing a variable/identifier
45926        if let Some(expr) = self.parse_var()? {
45927            return Ok(Some(expr));
45928        }
45929        // Allow keywords as identifiers in field context (e.g., "schema" as a field name)
45930        if self.check_keyword() {
45931            let token = self.advance();
45932            return Ok(Some(Expression::Identifier(Identifier {
45933                name: token.text,
45934                quoted: false,
45935                trailing_comments: Vec::new(),
45936                span: None,
45937            })));
45938        }
45939        Ok(None)
45940    }
45941
45942    /// parse_field_def - Ported from Python _parse_field_def
45943    /// Parses a field definition (column name + type + optional constraints)
45944    #[allow(unused_variables, unused_mut)]
45945    pub fn parse_field_def(&mut self) -> Result<Option<Expression>> {
45946        // First parse the field name (identifier)
45947        let field = self.parse_field()?;
45948
45949        if field.is_none() {
45950            return Ok(None);
45951        }
45952
45953        // Parse the column definition with the field as the name
45954        self.parse_column_def_with_field(field)
45955    }
45956
45957    /// Helper to parse a column definition with a pre-parsed field name
45958    fn parse_column_def_with_field(
45959        &mut self,
45960        field: Option<Expression>,
45961    ) -> Result<Option<Expression>> {
45962        if field.is_none() {
45963            return Ok(None);
45964        }
45965
45966        let this = field.unwrap();
45967
45968        // Get the identifier from the expression and preserve quoted-identifier state.
45969        let name_ident = match &this {
45970            Expression::Column(col) => col.name.clone(),
45971            Expression::Identifier(id) => id.clone(),
45972            Expression::Var(v) => Identifier::new(v.this.clone()),
45973            _ => return Ok(None),
45974        };
45975
45976        // Parse the data type using parse_data_type_optional (which handles unknown types gracefully)
45977        let data_type = match self.parse_data_type_optional()? {
45978            Some(dt) => dt,
45979            None => DataType::Unknown,
45980        };
45981
45982        // Create ColumnDef with default values
45983        let mut col_def = ColumnDef::new(name_ident.name.clone(), data_type);
45984        col_def.name = name_ident;
45985
45986        // Check for FOR ORDINALITY (JSON table columns)
45987        if self.match_text_seq(&["FOR", "ORDINALITY"]) {
45988            return Ok(Some(Expression::ColumnDef(Box::new(col_def))));
45989        }
45990
45991        // Parse constraints and extract specific constraint values
45992        loop {
45993            if let Some(constraint) = self.parse_column_constraint()? {
45994                // Check specific constraint types
45995                match &constraint {
45996                    Expression::NotNullColumnConstraint(_) => {
45997                        col_def.nullable = Some(false);
45998                        col_def.constraints.push(ColumnConstraint::NotNull);
45999                    }
46000                    Expression::PrimaryKeyColumnConstraint(_) => {
46001                        col_def.primary_key = true;
46002                        col_def.constraints.push(ColumnConstraint::PrimaryKey);
46003                    }
46004                    Expression::UniqueColumnConstraint(_) => {
46005                        col_def.unique = true;
46006                        col_def.constraints.push(ColumnConstraint::Unique);
46007                    }
46008                    Expression::DefaultColumnConstraint(dc) => {
46009                        col_def.default = Some((*dc.this).clone());
46010                        col_def
46011                            .constraints
46012                            .push(ColumnConstraint::Default((*dc.this).clone()));
46013                    }
46014                    Expression::AutoIncrementColumnConstraint(_) => {
46015                        col_def.auto_increment = true;
46016                    }
46017                    Expression::CommentColumnConstraint(_) => {
46018                        // Comment is a unit struct, we'd need the actual comment text
46019                    }
46020                    Expression::CheckColumnConstraint(cc) => {
46021                        col_def
46022                            .constraints
46023                            .push(ColumnConstraint::Check((*cc.this).clone()));
46024                    }
46025                    Expression::PathColumnConstraint(pc) => {
46026                        col_def
46027                            .constraints
46028                            .push(ColumnConstraint::Path((*pc.this).clone()));
46029                        col_def.constraint_order.push(ConstraintType::Path);
46030                    }
46031                    _ => {}
46032                }
46033            } else if matches!(
46034                self.config.dialect,
46035                Some(crate::dialects::DialectType::ClickHouse)
46036            ) && self.match_identifier("ALIAS")
46037            {
46038                // ClickHouse: ALIAS expr
46039                let expr = self.parse_or()?;
46040                col_def.alias_expr = Some(Box::new(expr));
46041            } else if matches!(
46042                self.config.dialect,
46043                Some(crate::dialects::DialectType::ClickHouse)
46044            ) && self.check(TokenType::Materialized)
46045                && !self.check_next(TokenType::View)
46046            {
46047                // ClickHouse: MATERIALIZED expr
46048                self.skip(); // consume MATERIALIZED
46049                let expr = self.parse_or()?;
46050                col_def.materialized_expr = Some(Box::new(expr));
46051            } else if matches!(
46052                self.config.dialect,
46053                Some(crate::dialects::DialectType::ClickHouse)
46054            ) && self.match_identifier("EPHEMERAL")
46055            {
46056                // ClickHouse: EPHEMERAL [expr]
46057                if !self.check(TokenType::Comma)
46058                    && !self.check(TokenType::RParen)
46059                    && !self.is_at_end()
46060                    && !self.check_identifier("CODEC")
46061                    && !self.check_identifier("TTL")
46062                    && !self.check(TokenType::Comment)
46063                {
46064                    let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
46065                    col_def.ephemeral = Some(Some(Box::new(expr)));
46066                } else {
46067                    col_def.ephemeral = Some(None);
46068                }
46069            } else if matches!(
46070                self.config.dialect,
46071                Some(crate::dialects::DialectType::ClickHouse)
46072            ) && self.check_identifier("CODEC")
46073            {
46074                // ClickHouse: CODEC(LZ4HC(9), ZSTD, DELTA)
46075                self.skip(); // consume CODEC
46076                self.expect(TokenType::LParen)?;
46077                let start = self.current;
46078                let mut depth = 1;
46079                while !self.is_at_end() && depth > 0 {
46080                    if self.check(TokenType::LParen) {
46081                        depth += 1;
46082                    }
46083                    if self.check(TokenType::RParen) {
46084                        depth -= 1;
46085                        if depth == 0 {
46086                            break;
46087                        }
46088                    }
46089                    self.skip();
46090                }
46091                let codec_text = self.tokens_to_sql(start, self.current);
46092                self.expect(TokenType::RParen)?;
46093                col_def.codec = Some(codec_text);
46094            } else if matches!(
46095                self.config.dialect,
46096                Some(crate::dialects::DialectType::ClickHouse)
46097            ) && self.match_identifier("TTL")
46098            {
46099                // ClickHouse: TTL expr
46100                let expr = self.parse_expression()?;
46101                col_def.ttl_expr = Some(Box::new(expr));
46102            } else {
46103                break;
46104            }
46105        }
46106
46107        Ok(Some(Expression::ColumnDef(Box::new(col_def))))
46108    }
46109
46110    /// parse_foreign_key - Implemented from Python _parse_foreign_key
46111    /// Calls: parse_key_constraint_options, parse_wrapped_id_vars, parse_references
46112    #[allow(unused_variables, unused_mut)]
46113    pub fn parse_foreign_key(&mut self) -> Result<Option<Expression>> {
46114        if self.match_text_seq(&["NO", "ACTION"]) {
46115            return Ok(Some(Expression::ForeignKey(Box::new(ForeignKey {
46116                expressions: Vec::new(),
46117                reference: None,
46118                delete: None,
46119                update: None,
46120                options: Vec::new(),
46121            }))));
46122        }
46123        Ok(None)
46124    }
46125
46126    /// parse_format_json - Implemented from Python _parse_format_json
46127    #[allow(unused_variables, unused_mut)]
46128    pub fn parse_format_json(&mut self) -> Result<Option<Expression>> {
46129        if self.match_text_seq(&["FORMAT", "JSON"]) {
46130            // Matched: FORMAT JSON
46131            return Ok(None);
46132        }
46133        Ok(None)
46134    }
46135
46136    /// parse_format_name - Snowflake FILE_FORMAT = format_name property
46137    /// Parses: format_name (string or identifier)
46138    #[allow(unused_variables, unused_mut)]
46139    pub fn parse_format_name(&mut self) -> Result<Option<Expression>> {
46140        // Try to parse a string first, then fall back to table parts
46141        let value = if let Some(s) = self.parse_string()? {
46142            s
46143        } else if let Some(tp) = self.parse_table_parts()? {
46144            tp
46145        } else {
46146            return Ok(None);
46147        };
46148
46149        Ok(Some(Expression::Property(Box::new(Property {
46150            this: Box::new(Expression::Identifier(Identifier::new(
46151                "FORMAT_NAME".to_string(),
46152            ))),
46153            value: Some(Box::new(value)),
46154        }))))
46155    }
46156
46157    /// parse_freespace - Teradata FREESPACE property
46158    /// Parses: FREESPACE = number [PERCENT]
46159    #[allow(unused_variables, unused_mut)]
46160    pub fn parse_freespace(&mut self) -> Result<Option<Expression>> {
46161        // Optionally consume = sign
46162        self.match_token(TokenType::Eq);
46163
46164        // Parse the number value
46165        let this = self.parse_number()?;
46166        if this.is_none() {
46167            return Ok(None);
46168        }
46169
46170        // Check for PERCENT keyword
46171        let percent = if self.match_token(TokenType::Percent) {
46172            Some(Box::new(Expression::Boolean(BooleanLiteral {
46173                value: true,
46174            })))
46175        } else {
46176            None
46177        };
46178
46179        Ok(Some(Expression::FreespaceProperty(Box::new(
46180            FreespaceProperty {
46181                this: Box::new(this.unwrap()),
46182                percent,
46183            },
46184        ))))
46185    }
46186
46187    /// parse_function - Ported from Python _parse_function
46188    /// Parses function calls like func_name(args) or {fn func_name(args)} (ODBC syntax)
46189    pub fn parse_function(&mut self) -> Result<Option<Expression>> {
46190        // Check for ODBC escape syntax: {fn function_call}
46191        let fn_syntax = if self.check(TokenType::LBrace) {
46192            if let Some(next) = self.tokens.get(self.current + 1) {
46193                if next.text.eq_ignore_ascii_case("FN") {
46194                    self.skip(); // consume {
46195                    self.skip(); // consume FN
46196                    true
46197                } else {
46198                    false
46199                }
46200            } else {
46201                false
46202            }
46203        } else {
46204            false
46205        };
46206
46207        let func = self.parse_function_call()?;
46208
46209        if fn_syntax {
46210            self.match_token(TokenType::RBrace);
46211        }
46212
46213        Ok(func)
46214    }
46215
46216    /// parse_function_args - Ported from Python _parse_function_args
46217    /// Parses the arguments inside a function call, handling aliases and key-value pairs
46218    pub fn parse_function_args_list(&mut self) -> Result<Vec<Expression>> {
46219        let mut args = Vec::new();
46220
46221        if self.check(TokenType::RParen) {
46222            return Ok(args);
46223        }
46224
46225        loop {
46226            // Try to parse expression with optional alias
46227            if let Some(expr) = self.parse_assignment()? {
46228                // Handle explicit AS alias inside function args (e.g. `tuple(1 AS "a", 2 AS "b")`)
46229                if self.match_token(TokenType::As) {
46230                    let alias_token = self.advance();
46231                    let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
46232                        // Preserve quoted identifiers
46233                        let raw = alias_token.text.clone();
46234                        let mut ident = Identifier::new(raw);
46235                        ident.quoted = true;
46236                        ident
46237                    } else {
46238                        Identifier::new(alias_token.text.clone())
46239                    };
46240                    args.push(Expression::Alias(Box::new(crate::expressions::Alias {
46241                        this: expr,
46242                        alias: alias_name,
46243                        column_aliases: Vec::new(),
46244                        pre_alias_comments: Vec::new(),
46245                        trailing_comments: Vec::new(),
46246                        inferred_type: None,
46247                    })));
46248                } else {
46249                    args.push(expr);
46250                }
46251            }
46252
46253            if !self.match_token(TokenType::Comma) {
46254                break;
46255            }
46256        }
46257
46258        Ok(args)
46259    }
46260
46261    /// parse_function_call - Ported from Python _parse_function_call
46262    /// Parses a function call expression like func_name(arg1, arg2, ...)
46263    pub fn parse_function_call(&mut self) -> Result<Option<Expression>> {
46264        if self.is_at_end() {
46265            return Ok(None);
46266        }
46267
46268        let token = self.peek().clone();
46269        let token_type = token.token_type.clone();
46270        let name = token.text.clone();
46271        let _upper_name = name.to_ascii_uppercase();
46272
46273        // Check for no-paren functions like CURRENT_DATE, CURRENT_TIMESTAMP
46274        if self.is_no_paren_function() {
46275            // Check if next token is NOT a paren (so it's used without parens)
46276            if !self.check_next(TokenType::LParen) {
46277                self.skip();
46278                return Ok(Some(Expression::Function(Box::new(Function {
46279                    name, // Preserve original case; generator handles normalization
46280                    args: Vec::new(),
46281                    distinct: false,
46282                    trailing_comments: Vec::new(),
46283                    use_bracket_syntax: false,
46284                    no_parens: true,
46285                    quoted: false,
46286                    span: None,
46287                    inferred_type: None,
46288                }))));
46289            }
46290        }
46291
46292        // Must be followed by left paren
46293        if !self.check_next(TokenType::LParen) {
46294            return Ok(None);
46295        }
46296
46297        // Token must be a valid function name token
46298        let is_valid_func_token = matches!(
46299            token_type,
46300            TokenType::Identifier
46301                | TokenType::Var
46302                | TokenType::If
46303                | TokenType::Left
46304                | TokenType::Right
46305                | TokenType::Insert
46306                | TokenType::Replace
46307                | TokenType::Row
46308                | TokenType::Index
46309        );
46310        if !is_valid_func_token {
46311            return Ok(None);
46312        }
46313
46314        self.skip(); // consume function name
46315        self.skip(); // consume (
46316
46317        // Check for DISTINCT keyword
46318        let distinct = self.match_token(TokenType::Distinct);
46319
46320        // Parse arguments
46321        let args = self.parse_function_args_list()?;
46322
46323        self.match_token(TokenType::RParen);
46324
46325        // Handle window specifications
46326        let func_expr = Expression::Function(Box::new(Function {
46327            name, // Preserve original case; generator handles normalization
46328            args,
46329            distinct,
46330            trailing_comments: Vec::new(),
46331            use_bracket_syntax: false,
46332            no_parens: false,
46333            quoted: false,
46334            span: None,
46335            inferred_type: None,
46336        }));
46337
46338        // Check for OVER clause (window function)
46339        if self.match_token(TokenType::Over) {
46340            // Parse window spec - create a simple WindowSpec
46341            if self.match_token(TokenType::LParen) {
46342                // Use parse_window_spec_inner to handle DISTRIBUTE BY/SORT BY (Hive)
46343                let spec = self.parse_window_spec_inner()?;
46344                self.expect(TokenType::RParen)?;
46345
46346                if let Some(spec_expr) = spec {
46347                    return Ok(Some(spec_expr));
46348                }
46349            }
46350        }
46351
46352        Ok(Some(func_expr))
46353    }
46354
46355    /// parse_function_parameter - Ported from Python _parse_function_parameter
46356    /// Parses a function parameter in CREATE FUNCTION (name type [DEFAULT expr])
46357    pub fn parse_function_parameter(&mut self) -> Result<Option<Expression>> {
46358        // Parse optional parameter mode (IN, OUT, INOUT)
46359        let _mode = if self.match_texts(&["IN"]) {
46360            if self.match_texts(&["OUT"]) {
46361                Some(ParameterMode::InOut)
46362            } else {
46363                Some(ParameterMode::In)
46364            }
46365        } else if self.match_texts(&["OUT"]) {
46366            Some(ParameterMode::Out)
46367        } else if self.match_texts(&["INOUT"]) {
46368            Some(ParameterMode::InOut)
46369        } else {
46370            None
46371        };
46372
46373        // Parse parameter name (optional in some dialects)
46374        let name_expr = self.parse_id_var()?;
46375        let name = name_expr.and_then(|n| match n {
46376            Expression::Identifier(id) => Some(id),
46377            _ => None,
46378        });
46379
46380        // Parse data type - returns Result<DataType>, not Result<Option<DataType>>
46381        // We need to handle the case where we can't parse a data type
46382        let data_type_result = self.parse_data_type();
46383        let _data_type = match data_type_result {
46384            Ok(dt) => dt,
46385            Err(_) => return Ok(None),
46386        };
46387
46388        // Parse optional DEFAULT value
46389        let _default = if self.match_token(TokenType::Default) || self.match_texts(&["="]) {
46390            self.parse_disjunction()?
46391        } else {
46392            None
46393        };
46394
46395        // Return the name as a Column expression
46396        Ok(Some(Expression::boxed_column(Column {
46397            name: Identifier {
46398                name: name.map(|n| n.name).unwrap_or_default(),
46399                quoted: false,
46400                trailing_comments: Vec::new(),
46401                span: None,
46402            },
46403            table: None,
46404            join_mark: false,
46405            trailing_comments: Vec::new(),
46406            span: None,
46407            inferred_type: None,
46408        })))
46409    }
46410
46411    /// parse_gap_fill - Ported from Python _parse_gap_fill
46412    #[allow(unused_variables, unused_mut)]
46413    /// parse_gap_fill - Parses GAP_FILL function for time series
46414    /// Example: GAP_FILL(TABLE t, ts_column, bucket_width, partitioning_columns, value_columns)
46415    pub fn parse_gap_fill(&mut self) -> Result<Option<Expression>> {
46416        // Optional TABLE keyword
46417        self.match_token(TokenType::Table);
46418
46419        // Parse the table reference
46420        let this = self.parse_table()?;
46421        if this.is_none() {
46422            return Ok(None);
46423        }
46424
46425        // Parse comma-separated arguments
46426        self.match_token(TokenType::Comma);
46427        let mut args = self.parse_expression_list()?;
46428
46429        // Extract arguments by position
46430        let ts_column = args.get(0).cloned().map(Box::new);
46431        let bucket_width = args.get(1).cloned().map(Box::new);
46432        let partitioning_columns = args.get(2).cloned().map(Box::new);
46433        let value_columns = args.get(3).cloned().map(Box::new);
46434
46435        Ok(Some(Expression::GapFill(Box::new(GapFill {
46436            this: Box::new(this.unwrap()),
46437            ts_column,
46438            bucket_width,
46439            partitioning_columns,
46440            value_columns,
46441            origin: None,
46442            ignore_nulls: None,
46443        }))))
46444    }
46445
46446    /// parse_semantic_view - Parse Snowflake SEMANTIC_VIEW function
46447    /// Example: SEMANTIC_VIEW(foo METRICS a.b, a.c DIMENSIONS a.b, a.c WHERE a.b > '1995-01-01')
46448    pub fn parse_semantic_view(&mut self) -> Result<Expression> {
46449        // Parse the table/view reference as a primary expression (identifier or qualified name)
46450        let this = self.parse_primary()?;
46451
46452        let mut metrics = None;
46453        let mut dimensions = None;
46454        let mut facts = None;
46455        let mut where_clause = None;
46456
46457        // Parse optional clauses: METRICS, DIMENSIONS, FACTS, WHERE
46458        while !self.check(TokenType::RParen) && !self.is_at_end() {
46459            if self.match_identifier("METRICS") {
46460                // Parse comma-separated expressions until next keyword or )
46461                let exprs = self.parse_semantic_view_list()?;
46462                metrics = Some(Box::new(Expression::Tuple(Box::new(Tuple {
46463                    expressions: exprs,
46464                }))));
46465            } else if self.match_identifier("DIMENSIONS") {
46466                let exprs = self.parse_semantic_view_list()?;
46467                dimensions = Some(Box::new(Expression::Tuple(Box::new(Tuple {
46468                    expressions: exprs,
46469                }))));
46470            } else if self.match_identifier("FACTS") {
46471                let exprs = self.parse_semantic_view_list()?;
46472                facts = Some(Box::new(Expression::Tuple(Box::new(Tuple {
46473                    expressions: exprs,
46474                }))));
46475            } else if self.match_token(TokenType::Where) {
46476                // Parse the WHERE expression
46477                where_clause = Some(Box::new(self.parse_expression()?));
46478                // WHERE is the last clause, break after parsing it
46479                break;
46480            } else {
46481                // Unknown token
46482                break;
46483            }
46484        }
46485
46486        Ok(Expression::SemanticView(Box::new(SemanticView {
46487            this: Box::new(this),
46488            metrics,
46489            dimensions,
46490            facts,
46491            where_: where_clause,
46492        })))
46493    }
46494
46495    /// Helper to parse comma-separated expression list for SEMANTIC_VIEW clauses
46496    /// Stops at METRICS, DIMENSIONS, FACTS, WHERE, or )
46497    /// Each element can have an optional AS alias: expr AS name
46498    fn parse_semantic_view_list(&mut self) -> Result<Vec<Expression>> {
46499        let first = self.parse_semantic_view_element()?;
46500        let mut exprs = vec![first];
46501        while self.match_token(TokenType::Comma) {
46502            // Check if next token is a keyword that starts a new clause
46503            if self.check_identifier("METRICS")
46504                || self.check_identifier("DIMENSIONS")
46505                || self.check_identifier("FACTS")
46506                || self.check(TokenType::Where)
46507                || self.check(TokenType::RParen)
46508            {
46509                break;
46510            }
46511            exprs.push(self.parse_semantic_view_element()?);
46512        }
46513        Ok(exprs)
46514    }
46515
46516    /// Parse a single SEMANTIC_VIEW element: expression [AS alias]
46517    fn parse_semantic_view_element(&mut self) -> Result<Expression> {
46518        let expr = self
46519            .parse_disjunction()?
46520            .ok_or_else(|| self.parse_error("Expected expression in SEMANTIC_VIEW clause"))?;
46521        // Check for optional explicit AS alias
46522        if self.match_token(TokenType::As) {
46523            let alias = self.expect_identifier_or_keyword_with_quoted()?;
46524            Ok(Expression::Alias(Box::new(crate::expressions::Alias {
46525                this: expr,
46526                alias,
46527                column_aliases: Vec::new(),
46528                pre_alias_comments: Vec::new(),
46529                trailing_comments: Vec::new(),
46530                inferred_type: None,
46531            })))
46532        } else {
46533            Ok(expr)
46534        }
46535    }
46536
46537    /// parse_grant_principal - Implemented from Python _parse_grant_principal
46538    /// Calls: parse_id_var
46539    #[allow(unused_variables, unused_mut)]
46540    pub fn parse_grant_principal(&mut self) -> Result<Option<Expression>> {
46541        if self.match_texts(&["ROLE", "GROUP"]) {
46542            // Matched one of: ROLE, GROUP
46543            return Ok(None);
46544        }
46545        Ok(None)
46546    }
46547
46548    /// parse_grant_privilege - Parse a single privilege in GRANT/REVOKE
46549    /// Parses: SELECT, INSERT, UPDATE(col1, col2), DELETE, etc.
46550    #[allow(unused_variables, unused_mut)]
46551    pub fn parse_grant_privilege(&mut self) -> Result<Option<Expression>> {
46552        // Collect privilege keywords (SELECT, INSERT, UPDATE, DELETE, ALL PRIVILEGES, etc.)
46553        let mut privilege_parts = Vec::new();
46554
46555        // Keep consuming keywords until we hit a follow token
46556        // Follow tokens are: comma, ON, left paren
46557        while !self.is_at_end() {
46558            // Check if we've hit a follow token
46559            if self.check(TokenType::Comma)
46560                || self.check(TokenType::On)
46561                || self.check(TokenType::LParen)
46562            {
46563                break;
46564            }
46565
46566            // Get the current token text
46567            let text = self.peek().text.to_ascii_uppercase();
46568            privilege_parts.push(text);
46569            self.skip();
46570        }
46571
46572        if privilege_parts.is_empty() {
46573            return Ok(None);
46574        }
46575
46576        let privilege_str = privilege_parts.join(" ");
46577
46578        // Check for column list in parentheses (e.g., UPDATE(col1, col2))
46579        let expressions = if self.match_token(TokenType::LParen) {
46580            let mut columns = Vec::new();
46581            loop {
46582                if let Some(col) = self.parse_column()? {
46583                    columns.push(col);
46584                } else {
46585                    break;
46586                }
46587                if !self.match_token(TokenType::Comma) {
46588                    break;
46589                }
46590            }
46591            self.match_token(TokenType::RParen);
46592            columns
46593        } else {
46594            Vec::new()
46595        };
46596
46597        Ok(Some(Expression::GrantPrivilege(Box::new(GrantPrivilege {
46598            this: Box::new(Expression::Identifier(Identifier::new(privilege_str))),
46599            expressions,
46600        }))))
46601    }
46602
46603    /// parse_grant_revoke_common - Parses common parts of GRANT/REVOKE statements
46604    /// Python: _parse_grant_revoke_common
46605    /// Returns a Tuple containing (privileges, kind, securable)
46606    pub fn parse_grant_revoke_common(&mut self) -> Result<Option<Expression>> {
46607        // Parse privileges (CSV of grant privileges)
46608        let mut privileges = Vec::new();
46609        loop {
46610            if let Some(priv_expr) = self.parse_grant_privilege()? {
46611                privileges.push(priv_expr);
46612            }
46613            if !self.match_token(TokenType::Comma) {
46614                break;
46615            }
46616        }
46617
46618        // Match ON keyword
46619        self.match_token(TokenType::On);
46620
46621        // Parse kind (TABLE, VIEW, SCHEMA, DATABASE, etc.)
46622        let kind = if self.match_texts(&[
46623            "TABLE",
46624            "VIEW",
46625            "SCHEMA",
46626            "DATABASE",
46627            "SEQUENCE",
46628            "FUNCTION",
46629            "PROCEDURE",
46630            "INDEX",
46631            "TYPE",
46632            "TABLESPACE",
46633            "ROLE",
46634            "USER",
46635        ]) {
46636            let kind_text = self.previous().text.to_ascii_uppercase();
46637            Some(Expression::Var(Box::new(Var { this: kind_text })))
46638        } else {
46639            None
46640        };
46641
46642        // Try to parse securable (table parts)
46643        let securable = self.parse_table_parts()?;
46644
46645        // Return as Tuple with three elements: privileges_list, kind, securable
46646        let privileges_expr = Expression::Tuple(Box::new(Tuple {
46647            expressions: privileges,
46648        }));
46649
46650        let mut result_exprs = vec![privileges_expr];
46651
46652        if let Some(k) = kind {
46653            result_exprs.push(k);
46654        } else {
46655            result_exprs.push(Expression::Null(Null));
46656        }
46657
46658        if let Some(s) = securable {
46659            result_exprs.push(s);
46660        } else {
46661            result_exprs.push(Expression::Null(Null));
46662        }
46663
46664        Ok(Some(Expression::Tuple(Box::new(Tuple {
46665            expressions: result_exprs,
46666        }))))
46667    }
46668
46669    /// parse_group - Parse GROUP BY clause
46670    /// Python: if not self._match(TokenType.GROUP_BY): return None; expressions = self._parse_csv(self._parse_disjunction)
46671    pub fn parse_group(&mut self) -> Result<Option<Expression>> {
46672        // Check for GROUP BY token (which should be parsed as Group + By tokens)
46673        if !self.match_token(TokenType::Group) {
46674            return Ok(None);
46675        }
46676        // Consume BY if present
46677        self.match_token(TokenType::By);
46678
46679        // Check for optional ALL/DISTINCT
46680        // Some(true) = ALL, Some(false) = DISTINCT, None = no modifier
46681        let all = if self.match_token(TokenType::All) {
46682            Some(true)
46683        } else if self.match_token(TokenType::Distinct) {
46684            Some(false)
46685        } else {
46686            None
46687        };
46688
46689        // Parse comma-separated expressions
46690        let mut expressions = Vec::new();
46691        loop {
46692            match self.parse_expression() {
46693                Ok(expr) => expressions.push(expr),
46694                Err(_) => break,
46695            }
46696            if !self.match_token(TokenType::Comma) {
46697                break;
46698            }
46699        }
46700
46701        // Handle TOTALS (ClickHouse)
46702        let totals = if self.match_text_seq(&["WITH", "TOTALS"]) {
46703            Some(Box::new(Expression::Boolean(BooleanLiteral {
46704                value: true,
46705            })))
46706        } else if self.match_text_seq(&["TOTALS"]) {
46707            Some(Box::new(Expression::Boolean(BooleanLiteral {
46708                value: true,
46709            })))
46710        } else {
46711            None
46712        };
46713
46714        Ok(Some(Expression::Group(Box::new(Group {
46715            expressions,
46716            grouping_sets: None,
46717            cube: None,
46718            rollup: None,
46719            totals,
46720            all,
46721        }))))
46722    }
46723
46724    /// parse_group_concat - Ported from Python _parse_group_concat
46725    #[allow(unused_variables, unused_mut)]
46726    /// parse_group_concat - Parses MySQL GROUP_CONCAT function
46727    /// Example: GROUP_CONCAT(DISTINCT col ORDER BY col SEPARATOR ',')
46728    pub fn parse_group_concat(&mut self) -> Result<Option<Expression>> {
46729        // Check for DISTINCT
46730        let distinct = self.match_token(TokenType::Distinct);
46731
46732        // Parse expression(s)
46733        let expr = self.parse_expression()?;
46734
46735        // Parse optional ORDER BY
46736        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
46737            let mut orderings = Vec::new();
46738            loop {
46739                let order_expr = self.parse_expression()?;
46740                let desc = if self.match_token(TokenType::Desc) {
46741                    true
46742                } else {
46743                    self.match_token(TokenType::Asc);
46744                    false
46745                };
46746                let nulls_first = if self.match_keywords(&[TokenType::Nulls, TokenType::First]) {
46747                    Some(true)
46748                } else if self.match_keywords(&[TokenType::Nulls, TokenType::Last]) {
46749                    Some(false)
46750                } else {
46751                    None
46752                };
46753                orderings.push(Ordered {
46754                    this: order_expr,
46755                    desc,
46756                    nulls_first,
46757                    explicit_asc: !desc,
46758                    with_fill: None,
46759                });
46760                if !self.match_token(TokenType::Comma) {
46761                    break;
46762                }
46763            }
46764            Some(orderings)
46765        } else {
46766            None
46767        };
46768
46769        // Parse optional SEPARATOR
46770        let separator = if self.match_token(TokenType::Separator) {
46771            self.parse_string()?
46772        } else {
46773            None
46774        };
46775
46776        Ok(Some(Expression::GroupConcat(Box::new(GroupConcatFunc {
46777            this: expr,
46778            separator,
46779            order_by,
46780            distinct,
46781            filter: None,
46782            inferred_type: None,
46783        }))))
46784    }
46785
46786    /// parse_grouping_set - Delegates to parse_grouping_sets
46787    #[allow(unused_variables, unused_mut)]
46788    pub fn parse_grouping_set(&mut self) -> Result<Option<Expression>> {
46789        self.parse_grouping_sets()
46790    }
46791
46792    /// parse_grouping_sets - Ported from Python _parse_grouping_sets
46793    /// Parses GROUPING SETS ((...), (...)) in GROUP BY
46794    #[allow(unused_variables, unused_mut)]
46795    pub fn parse_grouping_sets(&mut self) -> Result<Option<Expression>> {
46796        // Check for GROUPING SETS keyword
46797        if !self.match_text_seq(&["GROUPING", "SETS"]) {
46798            return Ok(None);
46799        }
46800
46801        // Parse wrapped grouping sets
46802        self.expect(TokenType::LParen)?;
46803        let mut expressions = Vec::new();
46804
46805        if !self.check(TokenType::RParen) {
46806            loop {
46807                // Each grouping set can be:
46808                // - A nested GROUPING SETS
46809                // - CUBE or ROLLUP
46810                // - A parenthesized list
46811                // - A single expression
46812                if let Some(nested) = self.parse_grouping_sets()? {
46813                    expressions.push(nested);
46814                } else if let Some(cube_rollup) = self.parse_cube_or_rollup()? {
46815                    expressions.push(cube_rollup);
46816                } else if self.match_token(TokenType::LParen) {
46817                    // Parenthesized group
46818                    let mut group = Vec::new();
46819                    if !self.check(TokenType::RParen) {
46820                        loop {
46821                            match self.parse_bitwise() {
46822                                Ok(Some(expr)) => group.push(expr),
46823                                Ok(None) => break,
46824                                Err(e) => return Err(e),
46825                            }
46826                            if !self.match_token(TokenType::Comma) {
46827                                break;
46828                            }
46829                        }
46830                    }
46831                    self.expect(TokenType::RParen)?;
46832                    expressions.push(Expression::Tuple(Box::new(Tuple { expressions: group })));
46833                } else {
46834                    // Single expression
46835                    match self.parse_bitwise() {
46836                        Ok(Some(expr)) => expressions.push(expr),
46837                        Ok(None) => break,
46838                        Err(e) => return Err(e),
46839                    }
46840                }
46841
46842                if !self.match_token(TokenType::Comma) {
46843                    break;
46844                }
46845            }
46846        }
46847
46848        self.expect(TokenType::RParen)?;
46849
46850        Ok(Some(Expression::GroupingSets(Box::new(GroupingSets {
46851            expressions,
46852        }))))
46853    }
46854
46855    /// parse_having - Parse HAVING clause
46856    /// Python: if not self._match(TokenType.HAVING): return None; return exp.Having(this=self._parse_disjunction())
46857    pub fn parse_having(&mut self) -> Result<Option<Expression>> {
46858        if !self.match_token(TokenType::Having) {
46859            return Ok(None);
46860        }
46861        // Parse the condition expression
46862        let condition = self.parse_expression()?;
46863        Ok(Some(Expression::Having(Box::new(Having {
46864            this: condition,
46865            comments: Vec::new(),
46866        }))))
46867    }
46868
46869    /// parse_having_max - Implemented from Python _parse_having_max
46870    /// Calls: parse_column
46871    #[allow(unused_variables, unused_mut)]
46872    pub fn parse_having_max(&mut self) -> Result<Option<Expression>> {
46873        if self.match_texts(&["MAX", "MIN"]) {
46874            // Matched one of: MAX, MIN
46875            return Ok(None);
46876        }
46877        Ok(None)
46878    }
46879
46880    /// parse_heredoc - Implemented from Python _parse_heredoc
46881    /// Parses dollar-quoted strings: $$content$$, $tag$content$tag$
46882    pub fn parse_heredoc(&mut self) -> Result<Option<Expression>> {
46883        // Check if current token is a HEREDOC_STRING type
46884        if self.match_token(TokenType::HeredocString) {
46885            let text = self.previous().text.clone();
46886            return Ok(Some(Expression::Heredoc(Box::new(Heredoc {
46887                this: Box::new(Expression::Literal(Box::new(Literal::String(text)))),
46888                tag: None,
46889            }))));
46890        }
46891
46892        // Try to parse $...$ or $tag$...$tag$
46893        if !self.match_text_seq(&["$"]) {
46894            return Ok(None);
46895        }
46896
46897        // Collect the tag text (if any) and the closing marker
46898        let mut tags = vec!["$".to_string()];
46899        let mut tag_text: Option<String> = None;
46900
46901        // Check if next token is connected (no whitespace) and collect tag
46902        if !self.is_at_end() {
46903            let next_text = self.peek().text.to_ascii_uppercase();
46904            if next_text == "$" {
46905                // Simple $$ ... $$ case
46906                self.skip();
46907                tags.push("$".to_string());
46908            } else {
46909                // $tag$ ... $tag$ case
46910                self.skip();
46911                tag_text = Some(next_text.clone());
46912                tags.push(next_text);
46913
46914                // Expect closing $
46915                if self.match_text_seq(&["$"]) {
46916                    tags.push("$".to_string());
46917                } else {
46918                    return Err(self.parse_error("No closing $ found"));
46919                }
46920            }
46921        }
46922
46923        // Now collect content until we find the closing tags
46924        let mut content_parts = Vec::new();
46925        let closing_tag = tags.join("");
46926
46927        while !self.is_at_end() {
46928            // Build current sequence to check for closing tag
46929            let current_text = self.peek().text.clone();
46930
46931            // Check if we've reached the closing tag
46932            if current_text == "$" || current_text.eq_ignore_ascii_case(&closing_tag) {
46933                // Try to match the full closing sequence
46934                let start_pos = self.current;
46935                let mut matched = true;
46936                for expected in &tags {
46937                    if self.is_at_end() || !self.peek().text.eq_ignore_ascii_case(expected) {
46938                        matched = false;
46939                        break;
46940                    }
46941                    self.skip();
46942                }
46943                if matched {
46944                    // Found the closing tag
46945                    let content = content_parts.join(" ");
46946                    return Ok(Some(Expression::Heredoc(Box::new(Heredoc {
46947                        this: Box::new(Expression::Literal(Box::new(Literal::String(content)))),
46948                        tag: tag_text
46949                            .map(|t| Box::new(Expression::Literal(Box::new(Literal::String(t))))),
46950                    }))));
46951                }
46952                // Not the closing tag, backtrack and add to content
46953                self.current = start_pos;
46954            }
46955
46956            content_parts.push(self.advance().text.clone());
46957        }
46958
46959        Err(self.parse_error(&format!("No closing {} found", closing_tag)))
46960    }
46961
46962    /// parse_hint_body - Delegates to parse_hint_fallback_to_string
46963    #[allow(unused_variables, unused_mut)]
46964    pub fn parse_hint_body(&mut self) -> Result<Option<Expression>> {
46965        self.parse_hint_fallback_to_string()
46966    }
46967
46968    /// parse_hint_fallback_to_string - Parses remaining hint tokens as a raw string
46969    /// Python: _parse_hint_fallback_to_string
46970    /// Used when structured hint parsing fails - collects all remaining tokens
46971    pub fn parse_hint_fallback_to_string(&mut self) -> Result<Option<Expression>> {
46972        // Collect all remaining tokens as a string
46973        let mut parts = Vec::new();
46974        while !self.is_at_end() {
46975            let token = self.advance();
46976            parts.push(token.text.clone());
46977        }
46978
46979        if parts.is_empty() {
46980            return Ok(None);
46981        }
46982
46983        let hint_text = parts.join(" ");
46984        Ok(Some(Expression::Hint(Box::new(Hint {
46985            expressions: vec![HintExpression::Raw(hint_text)],
46986        }))))
46987    }
46988
46989    /// parse_hint_function_call - Delegates to parse_function_call
46990    #[allow(unused_variables, unused_mut)]
46991    pub fn parse_hint_function_call(&mut self) -> Result<Option<Expression>> {
46992        self.parse_function_call()
46993    }
46994
46995    /// parse_historical_data - Snowflake AT/BEFORE time travel clauses
46996    /// Parses: AT(TIMESTAMP => expr) or BEFORE(STATEMENT => 'id') etc.
46997    /// Reference: https://docs.snowflake.com/en/sql-reference/constructs/at-before
46998    #[allow(unused_variables, unused_mut)]
46999    pub fn parse_historical_data(&mut self) -> Result<Option<Expression>> {
47000        // Save position for backtracking
47001        let start_index = self.current;
47002
47003        // Check for AT, BEFORE, or END keywords
47004        let this = if self.match_texts(&["AT", "BEFORE", "END"]) {
47005            self.previous().text.to_ascii_uppercase()
47006        } else {
47007            return Ok(None);
47008        };
47009
47010        // Expect opening paren and kind (OFFSET, STATEMENT, STREAM, TIMESTAMP, VERSION)
47011        if !self.match_token(TokenType::LParen) {
47012            // Backtrack if not the right pattern
47013            self.current = start_index;
47014            return Ok(None);
47015        }
47016
47017        let kind = if self.match_texts(&["OFFSET", "STATEMENT", "STREAM", "TIMESTAMP", "VERSION"]) {
47018            self.previous().text.to_ascii_uppercase()
47019        } else {
47020            // Backtrack if not the right pattern
47021            self.current = start_index;
47022            return Ok(None);
47023        };
47024
47025        // Expect => and expression
47026        if !self.match_token(TokenType::FArrow) {
47027            self.current = start_index;
47028            return Ok(None);
47029        }
47030
47031        let expression = self.parse_bitwise()?;
47032        if expression.is_none() {
47033            self.current = start_index;
47034            return Ok(None);
47035        }
47036
47037        self.match_token(TokenType::RParen); // Consume closing paren
47038
47039        Ok(Some(Expression::HistoricalData(Box::new(HistoricalData {
47040            this: Box::new(Expression::Identifier(Identifier::new(this))),
47041            kind,
47042            expression: Box::new(expression.unwrap()),
47043        }))))
47044    }
47045
47046    /// parse_id_var - Ported from Python _parse_id_var
47047    /// Parses an identifier or variable (more permissive than parse_identifier)
47048    #[allow(unused_variables, unused_mut)]
47049    pub fn parse_id_var(&mut self) -> Result<Option<Expression>> {
47050        // First try to parse a regular identifier
47051        if let Some(ident) = self.parse_identifier()? {
47052            return Ok(Some(ident));
47053        }
47054
47055        // Try to match Var token type
47056        if self.match_token(TokenType::Var) {
47057            let text = self.previous().text.clone();
47058            return Ok(Some(Expression::Identifier(Identifier {
47059                name: text,
47060                quoted: false,
47061                trailing_comments: Vec::new(),
47062                span: None,
47063            })));
47064        }
47065
47066        // Try to match string as identifier (some dialects allow this)
47067        if self.match_token(TokenType::String) {
47068            let text = self.previous().text.clone();
47069            return Ok(Some(Expression::Identifier(Identifier {
47070                name: text,
47071                quoted: true,
47072                trailing_comments: Vec::new(),
47073                span: None,
47074            })));
47075        }
47076
47077        // Accept keywords as identifiers in some contexts
47078        if self.check(TokenType::Select)
47079            || self.check(TokenType::From)
47080            || self.check(TokenType::Where)
47081            || self.check(TokenType::And)
47082            || self.check(TokenType::Or)
47083            || self.check(TokenType::Not)
47084            || self.check(TokenType::True)
47085            || self.check(TokenType::False)
47086            || self.check(TokenType::Null)
47087        {
47088            // Don't consume keywords as identifiers in parse_id_var
47089            return Ok(None);
47090        }
47091
47092        Ok(None)
47093    }
47094
47095    /// parse_identifier - Parse quoted identifier
47096    /// Python: if self._match(TokenType.IDENTIFIER): return self._identifier_expression(quoted=True)
47097    pub fn parse_identifier(&mut self) -> Result<Option<Expression>> {
47098        // Match quoted identifiers (e.g., "column_name" or `column_name`)
47099        if self.match_token(TokenType::QuotedIdentifier) || self.match_token(TokenType::Identifier)
47100        {
47101            let text = self.previous().text.clone();
47102            let quoted = self.previous().token_type == TokenType::QuotedIdentifier;
47103            return Ok(Some(Expression::Identifier(Identifier {
47104                name: text,
47105                quoted,
47106                trailing_comments: Vec::new(),
47107                span: None,
47108            })));
47109        }
47110        Ok(None)
47111    }
47112
47113    /// Parse IF expression
47114    /// IF(condition, true_value, false_value) - function style
47115    /// IF condition THEN true_value ELSE false_value END - statement style
47116    pub fn parse_if(&mut self) -> Result<Option<Expression>> {
47117        // TSQL/Fabric: IF (cond) BEGIN ... END is a statement, not a function.
47118        // Parse condition, strip outer parens, then capture rest as command.
47119        if matches!(
47120            self.config.dialect,
47121            Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)
47122        ) && self.check(TokenType::LParen)
47123        {
47124            // Parse the parenthesized condition using balanced paren matching
47125            let cond_start = self.current;
47126            self.skip(); // consume opening (
47127            let mut depth = 1;
47128            while depth > 0 && !self.is_at_end() {
47129                if self.check(TokenType::LParen) {
47130                    depth += 1;
47131                } else if self.check(TokenType::RParen) {
47132                    depth -= 1;
47133                    if depth == 0 {
47134                        break;
47135                    }
47136                }
47137                self.skip();
47138            }
47139            // Extract condition text from source (inside outer parens)
47140            let cond_text = if let Some(ref source) = self.source {
47141                let inner_start = self.tokens[cond_start + 1].span.start;
47142                let inner_end = self.tokens[self.current].span.start;
47143                source[inner_start..inner_end].trim().to_string()
47144            } else {
47145                self.tokens_to_sql(cond_start + 1, self.current)
47146            };
47147            self.skip(); // consume closing )
47148
47149            // Now collect the rest (BEGIN...END) as raw text
47150            let body_start = self.current;
47151            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
47152                self.skip();
47153            }
47154            let body_text = if let Some(ref source) = self.source {
47155                let start_span = self.tokens[body_start].span.start;
47156                let end_span = if self.current > 0 {
47157                    self.tokens[self.current - 1].span.end
47158                } else {
47159                    start_span
47160                };
47161                source[start_span..end_span].trim().to_string()
47162            } else {
47163                self.tokens_to_sql(body_start, self.current)
47164            };
47165            let command_text = format!("IF {} {}", cond_text, body_text);
47166            return Ok(Some(Expression::Command(Box::new(
47167                crate::expressions::Command { this: command_text },
47168            ))));
47169        }
47170
47171        // Function style: IF(cond, true, false)
47172        if self.match_token(TokenType::LParen) {
47173            // ClickHouse: if() with zero args is valid (used in test queries)
47174            if self.check(TokenType::RParen) {
47175                self.skip(); // consume RParen
47176                return Ok(Some(Expression::Function(Box::new(Function {
47177                    name: "IF".to_string(),
47178                    args: vec![],
47179                    distinct: false,
47180                    trailing_comments: Vec::new(),
47181                    use_bracket_syntax: false,
47182                    no_parens: false,
47183                    quoted: false,
47184                    span: None,
47185                    inferred_type: None,
47186                }))));
47187            }
47188            let args = self.parse_expression_list()?;
47189            self.expect(TokenType::RParen)?;
47190
47191            if args.len() == 3 {
47192                return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
47193                    original_name: None,
47194                    condition: args[0].clone(),
47195                    true_value: args[1].clone(),
47196                    false_value: Some(args[2].clone()),
47197                    inferred_type: None,
47198                }))));
47199            } else if args.len() == 2 {
47200                return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
47201                    original_name: None,
47202                    condition: args[0].clone(),
47203                    true_value: args[1].clone(),
47204                    false_value: None,
47205                    inferred_type: None,
47206                }))));
47207            } else if args.len() == 1 {
47208                return Ok(Some(Expression::Function(Box::new(Function {
47209                    name: "IF".to_string(),
47210                    args,
47211                    distinct: false,
47212                    trailing_comments: Vec::new(),
47213                    use_bracket_syntax: false,
47214                    no_parens: false,
47215                    quoted: false,
47216                    span: None,
47217                    inferred_type: None,
47218                }))));
47219            } else {
47220                return Err(self.parse_error("IF function requires 2 or 3 arguments"));
47221            }
47222        }
47223
47224        // TSQL: IF OBJECT_ID(...) IS NOT NULL [BEGIN] DROP TABLE x [; END] -> DROP TABLE IF EXISTS x
47225        if matches!(
47226            self.config.dialect,
47227            Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)
47228        ) {
47229            let saved = self.current;
47230            if self.match_text_seq(&["OBJECT_ID"]) {
47231                // Capture the OBJECT_ID arguments text for TSQL round-trip
47232                let object_id_args_text = if self.match_token(TokenType::LParen) {
47233                    let args_start = self.current;
47234                    let args = self.parse_expression_list()?;
47235                    // Reconstruct args text from source
47236                    let args_text = if let Some(ref source) = self.source {
47237                        let start_span = self.tokens[args_start].span.start;
47238                        let end_span = self.tokens[self.current].span.start;
47239                        source[start_span..end_span].trim().to_string()
47240                    } else {
47241                        // Fallback: generate from parsed expressions
47242                        args.iter()
47243                            .map(|a| format!("{:?}", a))
47244                            .collect::<Vec<_>>()
47245                            .join(", ")
47246                    };
47247                    let _ = self.match_token(TokenType::RParen);
47248                    Some(args_text)
47249                } else {
47250                    None
47251                };
47252                if self.match_text_seq(&["IS", "NOT", "NULL"]) {
47253                    // Check for DROP directly or BEGIN ... DROP ... END
47254                    let has_begin = self.match_token(TokenType::Begin);
47255                    if self.check(TokenType::Drop) {
47256                        // Parse DROP TABLE, forcing if_exists = true
47257                        self.skip(); // consume DROP
47258                        if self.match_token(TokenType::Table) {
47259                            // Parse table names
47260                            let mut names = Vec::new();
47261                            loop {
47262                                names.push(self.parse_table_ref()?);
47263                                if !self.match_token(TokenType::Comma) {
47264                                    break;
47265                                }
47266                            }
47267                            // If we had BEGIN, consume optional ; and END
47268                            if has_begin {
47269                                let _ = self.match_token(TokenType::Semicolon);
47270                                let _ = self.match_token(TokenType::End);
47271                            }
47272                            return Ok(Some(Expression::DropTable(Box::new(
47273                                crate::expressions::DropTable {
47274                                    names,
47275                                    if_exists: true,
47276                                    cascade: false,
47277                                    cascade_constraints: false,
47278                                    purge: false,
47279                                    leading_comments: Vec::new(),
47280                                    object_id_args: object_id_args_text,
47281                                    sync: false,
47282                                    iceberg: false,
47283                                    restrict: false,
47284                                },
47285                            ))));
47286                        }
47287                    }
47288                }
47289                // Retreat if pattern didn't match
47290                self.current = saved;
47291            }
47292        }
47293
47294        // Statement style: IF cond THEN true [ELSE false] END/ENDIF
47295        // Use parse_disjunction (parse_or) for condition - same as Python sqlglot
47296        // This ensures we stop at THEN rather than consuming too much
47297        let condition = match self.parse_disjunction()? {
47298            Some(c) => c,
47299            None => return Ok(None),
47300        };
47301
47302        if !self.match_token(TokenType::Then) {
47303            // Not statement style, return as just the expression parsed
47304            return Ok(Some(condition));
47305        }
47306
47307        // Parse true value - use parse_disjunction to stop at ELSE/END
47308        let true_value = match self.parse_disjunction()? {
47309            Some(v) => v,
47310            None => return Err(self.parse_error("Expected expression after THEN")),
47311        };
47312
47313        let false_value = if self.match_token(TokenType::Else) {
47314            match self.parse_disjunction()? {
47315                Some(v) => Some(v),
47316                None => return Err(self.parse_error("Expected expression after ELSE")),
47317            }
47318        } else {
47319            None
47320        };
47321
47322        // Consume END or ENDIF (Exasol tokenizes ENDIF as END)
47323        self.match_token(TokenType::End);
47324
47325        Ok(Some(Expression::IfFunc(Box::new(IfFunc {
47326            original_name: None,
47327            condition,
47328            true_value,
47329            false_value,
47330            inferred_type: None,
47331        }))))
47332    }
47333
47334    /// parse_in - Ported from Python _parse_in
47335    /// Parses IN expression: expr IN (values...) or expr IN (subquery)
47336    /// Can also parse standalone IN list after IN keyword has been matched
47337    #[allow(unused_variables, unused_mut)]
47338    pub fn parse_in(&mut self) -> Result<Option<Expression>> {
47339        // If we're at IN keyword, parse what follows
47340        if self.match_token(TokenType::In) {
47341            return Err(self.parse_error("Expected expression before IN"));
47342        }
47343
47344        // Try to parse as a complete expression: left IN (...)
47345        let saved_pos = self.current;
47346
47347        // Parse the left side expression
47348        match self.parse_bitwise() {
47349            Ok(Some(left_expr)) => {
47350                // Check for optional NOT
47351                let negate = self.match_token(TokenType::Not);
47352
47353                // Expect IN keyword
47354                if self.match_token(TokenType::In) {
47355                    let in_result = self.parse_in_with_expr(Some(left_expr))?;
47356                    return Ok(Some(if negate {
47357                        Expression::Not(Box::new(UnaryOp {
47358                            this: in_result,
47359                            inferred_type: None,
47360                        }))
47361                    } else {
47362                        in_result
47363                    }));
47364                }
47365
47366                // Not an IN expression, restore position
47367                self.current = saved_pos;
47368                Ok(None)
47369            }
47370            Ok(None) => {
47371                self.current = saved_pos;
47372                Ok(None)
47373            }
47374            Err(_) => {
47375                self.current = saved_pos;
47376                Ok(None)
47377            }
47378        }
47379    }
47380
47381    /// parse_index - Implemented from Python _parse_index
47382    /// Calls: parse_index_params, parse_id_var
47383    #[allow(unused_variables, unused_mut)]
47384    pub fn parse_index(&mut self) -> Result<Option<Expression>> {
47385        if self.match_text_seq(&["PRIMARY"]) {
47386            return Ok(Some(Expression::Index(Box::new(Index {
47387                this: None,
47388                table: None,
47389                unique: false,
47390                primary: None,
47391                amp: None,
47392                params: Vec::new(),
47393            }))));
47394        }
47395        if self.match_text_seq(&["AMP"]) {
47396            // Matched: AMP
47397            return Ok(None);
47398        }
47399        Ok(None)
47400    }
47401
47402    /// parse_index_params - Implemented from Python _parse_index_params
47403    /// Calls: parse_where, parse_wrapped_properties, parse_wrapped_id_vars
47404    #[allow(unused_variables, unused_mut)]
47405    pub fn parse_index_params(&mut self) -> Result<Option<Expression>> {
47406        if self.match_text_seq(&["INCLUDE"]) {
47407            return Ok(Some(Expression::IndexParameters(Box::new(
47408                IndexParameters {
47409                    using: None,
47410                    include: None,
47411                    columns: Vec::new(),
47412                    with_storage: None,
47413                    partition_by: None,
47414                    tablespace: None,
47415                    where_: None,
47416                    on: None,
47417                },
47418            ))));
47419        }
47420        if self.match_text_seq(&["USING", "INDEX", "TABLESPACE"]) {
47421            // Matched: USING INDEX TABLESPACE
47422            return Ok(None);
47423        }
47424        Ok(None)
47425    }
47426
47427    /// parse_initcap - Ported from Python _parse_initcap
47428    #[allow(unused_variables, unused_mut)]
47429    /// parse_initcap - Parses INITCAP function
47430    /// Example: INITCAP(str) or INITCAP(str, delimiter)
47431    pub fn parse_initcap(&mut self) -> Result<Option<Expression>> {
47432        // Parse the first argument (string to capitalize)
47433        let args = self.parse_expression_list()?;
47434
47435        if args.is_empty() {
47436            return Ok(None);
47437        }
47438
47439        // Initcap is a UnaryFunc
47440        Ok(Some(Expression::Initcap(Box::new(UnaryFunc::new(
47441            args.into_iter().next().unwrap(),
47442        )))))
47443    }
47444
47445    /// parse_inline - Implemented from Python _parse_inline
47446    #[allow(unused_variables, unused_mut)]
47447    pub fn parse_inline(&mut self) -> Result<Option<Expression>> {
47448        if self.match_text_seq(&["LENGTH"]) {
47449            // Matched: LENGTH
47450            return Ok(None);
47451        }
47452        Ok(None)
47453    }
47454
47455    /// parse_insert_table - Parse table reference for INSERT statement
47456    /// Parses: table_name [schema] [partition] [alias]
47457    /// This method is a simple wrapper around parse_table for INSERT context
47458    #[allow(unused_variables, unused_mut)]
47459    pub fn parse_insert_table(&mut self) -> Result<Option<Expression>> {
47460        // Parse the table reference - parse_table handles aliases
47461        self.parse_table()
47462    }
47463
47464    /// parse_interpolate - Implemented from Python _parse_interpolate
47465    /// Parses INTERPOLATE clause for ClickHouse ORDER BY WITH FILL
47466    pub fn parse_interpolate(&mut self) -> Result<Option<Expression>> {
47467        if !self.match_text_seq(&["INTERPOLATE"]) {
47468            return Ok(None);
47469        }
47470
47471        // Parse wrapped CSV of name-as-expression pairs
47472        if self.match_token(TokenType::LParen) {
47473            let mut expressions = Vec::new();
47474            loop {
47475                if let Some(expr) = self.parse_name_as_expression()? {
47476                    expressions.push(expr);
47477                }
47478                if !self.match_token(TokenType::Comma) {
47479                    break;
47480                }
47481            }
47482            self.match_token(TokenType::RParen);
47483
47484            if expressions.is_empty() {
47485                return Ok(None);
47486            }
47487
47488            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
47489        }
47490
47491        Ok(None)
47492    }
47493
47494    /// parse_interval - Creates Interval expression
47495    /// Parses INTERVAL expressions: INTERVAL '1 day', INTERVAL 1 MONTH, etc.
47496    #[allow(unused_variables, unused_mut)]
47497    pub fn parse_interval(&mut self) -> Result<Option<Expression>> {
47498        // Delegate to the existing try_parse_interval method
47499        self.try_parse_interval()
47500    }
47501
47502    /// parse_interval_span - Implemented from Python _parse_interval_span
47503    /// Calls: parse_function
47504    #[allow(unused_variables, unused_mut)]
47505    pub fn parse_interval_span(&mut self) -> Result<Option<Expression>> {
47506        if self.match_text_seq(&["TO"]) {
47507            return Ok(Some(Expression::Var(Box::new(Var {
47508                this: String::new(),
47509            }))));
47510        }
47511        if self.match_text_seq(&["TO"]) {
47512            // Matched: TO
47513            return Ok(None);
47514        }
47515        Ok(None)
47516    }
47517
47518    /// parse_into - Implemented from Python _parse_into
47519    /// Parses: INTO [TEMPORARY] [UNLOGGED] [TABLE] table_name
47520    /// Returns the table expression for the INTO clause
47521    #[allow(unused_variables, unused_mut)]
47522    pub fn parse_into(&mut self) -> Result<Option<Expression>> {
47523        if !self.match_token(TokenType::Into) {
47524            return Ok(None);
47525        }
47526
47527        // Optional TEMPORARY
47528        let _temp = self.match_token(TokenType::Temporary);
47529
47530        // Optional UNLOGGED
47531        let _unlogged = self.match_text_seq(&["UNLOGGED"]);
47532
47533        // Optional TABLE keyword
47534        let _ = self.match_token(TokenType::Table);
47535
47536        // Parse the table name
47537        self.parse_table_parts()
47538    }
47539
47540    /// parse_introducer - Parses MySQL introducer expression (_charset'string')
47541    /// Python: _parse_introducer
47542    /// Format: _charset 'literal'
47543    pub fn parse_introducer(&mut self) -> Result<Option<Expression>> {
47544        // We expect to have already consumed the introducer token (e.g., _utf8)
47545        let token = self.previous().clone();
47546
47547        // Try to parse a primary expression (usually a string literal)
47548        // parse_primary returns Expression (not Option), so we use it directly
47549        let literal = self.parse_primary()?;
47550
47551        // Check if it's a null expression (indicating nothing was parsed)
47552        match &literal {
47553            Expression::Null(_) => {
47554                // Just return as an identifier
47555                Ok(Some(Expression::Identifier(Identifier {
47556                    name: token.text.clone(),
47557                    quoted: false,
47558                    trailing_comments: Vec::new(),
47559                    span: None,
47560                })))
47561            }
47562            _ => Ok(Some(Expression::Introducer(Box::new(Introducer {
47563                this: Box::new(Expression::Identifier(Identifier {
47564                    name: token.text.clone(),
47565                    quoted: false,
47566                    trailing_comments: Vec::new(),
47567                    span: None,
47568                })),
47569                expression: Box::new(literal),
47570            })))),
47571        }
47572    }
47573
47574    /// parse_is - Implemented from Python _parse_is
47575    /// Calls: parse_null, parse_bitwise
47576    #[allow(unused_variables, unused_mut)]
47577    pub fn parse_is(&mut self) -> Result<Option<Expression>> {
47578        if self.match_text_seq(&["DISTINCT", "FROM"]) {
47579            return Ok(Some(Expression::JSON(Box::new(JSON {
47580                this: None,
47581                with_: None,
47582                unique: false,
47583            }))));
47584        }
47585        if self.match_text_seq(&["WITH"]) {
47586            // Matched: WITH
47587            return Ok(None);
47588        }
47589        if self.match_text_seq(&["WITHOUT"]) {
47590            // Matched: WITHOUT
47591            return Ok(None);
47592        }
47593        Ok(None)
47594    }
47595
47596    /// parse_join - Ported from Python _parse_join
47597    /// Parses a single JOIN clause: [method] [side] [kind] JOIN table [ON condition | USING (columns)]
47598    /// Returns the Join wrapped in an Expression, or None if no join is found
47599    #[allow(unused_variables, unused_mut)]
47600    pub fn parse_join(&mut self) -> Result<Option<Expression>> {
47601        // Check for comma-style implicit join
47602        if self.match_token(TokenType::Comma) {
47603            if let Ok(Some(table)) = self.parse_table() {
47604                return Ok(Some(Expression::Join(Box::new(Join {
47605                    this: table,
47606                    on: None,
47607                    using: Vec::new(),
47608                    kind: JoinKind::Implicit,
47609                    use_inner_keyword: false,
47610                    use_outer_keyword: false,
47611                    deferred_condition: false,
47612                    join_hint: None,
47613                    match_condition: None,
47614                    pivots: Vec::new(),
47615                    comments: Vec::new(),
47616                    nesting_group: 0,
47617                    directed: false,
47618                }))));
47619            }
47620            return Ok(None);
47621        }
47622
47623        // Try to parse join kind (INNER, LEFT, RIGHT, FULL, CROSS, etc.)
47624        let saved_pos = self.current;
47625        if let Some((kind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint)) =
47626            self.try_parse_join_kind()
47627        {
47628            // Collect comments from tokens consumed by try_parse_join_kind
47629            let mut join_comments = Vec::new();
47630            for i in saved_pos..self.current {
47631                if i < self.tokens.len() {
47632                    join_comments.extend(self.tokens[i].trailing_comments.iter().cloned());
47633                }
47634            }
47635
47636            // If kind requires JOIN keyword, expect it
47637            if needs_join_keyword && !self.match_token(TokenType::Join) {
47638                self.current = saved_pos;
47639                return Ok(None);
47640            }
47641
47642            // Parse the table being joined
47643            let table = self.parse_table_expression()?;
47644
47645            // Parse ON or USING condition
47646            let (on, using) = if self.match_token(TokenType::On) {
47647                (Some(self.parse_expression()?), Vec::new())
47648            } else if self.match_token(TokenType::Using) {
47649                let has_parens = self.match_token(TokenType::LParen);
47650                // Use parse_using_column_list to handle qualified names like t1.col
47651                let cols = self.parse_using_column_list()?;
47652                if has_parens {
47653                    self.expect(TokenType::RParen)?;
47654                }
47655                (None, cols)
47656            } else {
47657                (None, Vec::new())
47658            };
47659
47660            return Ok(Some(Expression::Join(Box::new(Join {
47661                this: table,
47662                on,
47663                using,
47664                kind,
47665                use_inner_keyword,
47666                use_outer_keyword,
47667                deferred_condition: false,
47668                join_hint,
47669                match_condition: None,
47670                pivots: Vec::new(),
47671                comments: join_comments,
47672                nesting_group: 0,
47673                directed: false,
47674            }))));
47675        }
47676
47677        // Check for CROSS APPLY / OUTER APPLY (SQL Server)
47678        if self.match_text_seq(&["CROSS", "APPLY"]) || self.match_text_seq(&["OUTER", "APPLY"]) {
47679            let is_outer = self.previous().text.eq_ignore_ascii_case("OUTER");
47680            let table = self.parse_table_expression()?;
47681            return Ok(Some(Expression::Join(Box::new(Join {
47682                this: table,
47683                on: None,
47684                using: Vec::new(),
47685                kind: if is_outer {
47686                    JoinKind::Outer
47687                } else {
47688                    JoinKind::Cross
47689                },
47690                use_inner_keyword: false,
47691                use_outer_keyword: is_outer,
47692                deferred_condition: false,
47693                join_hint: None,
47694                match_condition: None,
47695                pivots: Vec::new(),
47696                comments: Vec::new(),
47697                nesting_group: 0,
47698                directed: false,
47699            }))));
47700        }
47701
47702        Ok(None)
47703    }
47704
47705    /// parse_join_hint - Spark/Hive join hints (BROADCAST, MERGE, SHUFFLE_HASH, etc.)
47706    /// Parses: HINT_NAME(table1, table2, ...)
47707    /// hint_name should be the already matched hint keyword (BROADCAST, MAPJOIN, etc.)
47708    #[allow(unused_variables, unused_mut)]
47709    pub fn parse_join_hint(&mut self, hint_name: &str) -> Result<Option<Expression>> {
47710        // Parse comma-separated list of tables
47711        let mut tables = Vec::new();
47712        loop {
47713            if let Some(table) = self.parse_table()? {
47714                tables.push(table);
47715            } else {
47716                break;
47717            }
47718            if !self.match_token(TokenType::Comma) {
47719                break;
47720            }
47721        }
47722
47723        Ok(Some(Expression::JoinHint(Box::new(JoinHint {
47724            this: Box::new(Expression::Identifier(Identifier::new(
47725                hint_name.to_ascii_uppercase(),
47726            ))),
47727            expressions: tables,
47728        }))))
47729    }
47730
47731    /// parse_join_parts - Ported from Python _parse_join_parts
47732    /// Returns (method, side, kind) where each is an optional string
47733    /// method: ASOF, NATURAL, POSITIONAL
47734    /// side: LEFT, RIGHT, FULL
47735    /// kind: ANTI, CROSS, INNER, OUTER, SEMI
47736    pub fn parse_join_parts(&mut self) -> (Option<String>, Option<String>, Option<String>) {
47737        // Parse join method (ASOF, NATURAL, POSITIONAL)
47738        let method = if self.match_texts(&["ASOF", "NATURAL", "POSITIONAL"]) {
47739            Some(self.previous().text.to_ascii_uppercase())
47740        } else {
47741            None
47742        };
47743
47744        // Parse join side (LEFT, RIGHT, FULL)
47745        let side = if self.match_texts(&["LEFT", "RIGHT", "FULL"]) {
47746            Some(self.previous().text.to_ascii_uppercase())
47747        } else {
47748            None
47749        };
47750
47751        // Parse join kind (ANTI, CROSS, INNER, OUTER, SEMI)
47752        let kind = if self.match_texts(&["ANTI", "CROSS", "INNER", "OUTER", "SEMI"]) {
47753            Some(self.previous().text.to_ascii_uppercase())
47754        } else if self.match_token(TokenType::StraightJoin) {
47755            Some("STRAIGHT_JOIN".to_string())
47756        } else {
47757            None
47758        };
47759
47760        (method, side, kind)
47761    }
47762
47763    /// parse_journal - Parses JOURNAL property (Teradata)
47764    /// Python: _parse_journal
47765    /// Creates a JournalProperty expression
47766    pub fn parse_journal(&mut self) -> Result<Option<Expression>> {
47767        self.parse_journal_impl(false, false, false, false, false)
47768    }
47769
47770    /// Implementation of parse_journal with options
47771    pub fn parse_journal_impl(
47772        &mut self,
47773        no: bool,
47774        dual: bool,
47775        before: bool,
47776        local: bool,
47777        after: bool,
47778    ) -> Result<Option<Expression>> {
47779        Ok(Some(Expression::JournalProperty(Box::new(
47780            JournalProperty {
47781                no: if no {
47782                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47783                        value: true,
47784                    })))
47785                } else {
47786                    None
47787                },
47788                dual: if dual {
47789                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47790                        value: true,
47791                    })))
47792                } else {
47793                    None
47794                },
47795                before: if before {
47796                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47797                        value: true,
47798                    })))
47799                } else {
47800                    None
47801                },
47802                local: if local {
47803                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47804                        value: true,
47805                    })))
47806                } else {
47807                    None
47808                },
47809                after: if after {
47810                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47811                        value: true,
47812                    })))
47813                } else {
47814                    None
47815                },
47816            },
47817        ))))
47818    }
47819
47820    /// parse_json_column_def - Implemented from Python _parse_json_column_def
47821    /// Calls: parse_string, parse_json_schema, parse_id_var
47822    #[allow(unused_variables, unused_mut)]
47823    pub fn parse_json_column_def(&mut self) -> Result<Option<Expression>> {
47824        if self.match_text_seq(&["NESTED"]) {
47825            return Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
47826                this: None,
47827                kind: None,
47828                path: None,
47829                nested_schema: None,
47830                ordinality: None,
47831            }))));
47832        }
47833        if self.match_text_seq(&["PATH"]) {
47834            // Matched: PATH
47835            return Ok(None);
47836        }
47837        Ok(None)
47838    }
47839
47840    /// parse_json_key_value - Implemented from Python _parse_json_key_value
47841    #[allow(unused_variables, unused_mut)]
47842    /// parse_json_key_value - Parses a JSON key-value pair
47843    /// Python: _parse_json_key_value
47844    /// Format: [KEY] key [: | VALUE] value
47845    pub fn parse_json_key_value(&mut self) -> Result<Option<Expression>> {
47846        // Optional KEY keyword
47847        self.match_text_seq(&["KEY"]);
47848
47849        // Parse the key expression
47850        let key = self.parse_column()?;
47851
47852        // Match separator (colon, comma, or VALUE keyword)
47853        let _ = self.match_token(TokenType::Colon)
47854            || self.match_token(TokenType::Comma)
47855            || self.match_text_seq(&["VALUE"]);
47856
47857        // Optional VALUE keyword
47858        self.match_text_seq(&["VALUE"]);
47859
47860        // Parse the value expression
47861        let value = self.parse_bitwise()?;
47862
47863        // If neither key nor value, return None
47864        match (key, value) {
47865            (None, None) => Ok(None),
47866            (Some(k), None) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
47867                this: Box::new(k),
47868                expression: Box::new(Expression::Null(Null)),
47869            })))),
47870            (None, Some(v)) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
47871                this: Box::new(Expression::Null(Null)),
47872                expression: Box::new(v),
47873            })))),
47874            (Some(k), Some(v)) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
47875                this: Box::new(k),
47876                expression: Box::new(v),
47877            })))),
47878        }
47879    }
47880
47881    /// parse_json_object - Parses JSON_OBJECT function
47882    /// Python: _parse_json_object
47883    /// Handles both JSON_OBJECT and JSON_OBJECTAGG
47884    pub fn parse_json_object(&mut self) -> Result<Option<Expression>> {
47885        self.parse_json_object_impl(false)
47886    }
47887
47888    /// Implementation of JSON object parsing with aggregate flag
47889    pub fn parse_json_object_impl(&mut self, agg: bool) -> Result<Option<Expression>> {
47890        // Try to parse a star expression
47891        let star = self.parse_star()?;
47892
47893        // Parse expressions: either star or comma-separated key-value pairs
47894        let expressions = if let Some(star_expr) = star {
47895            vec![star_expr]
47896        } else {
47897            // Parse comma-separated JSON key-value pairs
47898            let mut exprs = Vec::new();
47899            loop {
47900                if let Some(kv) = self.parse_json_key_value()? {
47901                    // Wrap with FORMAT JSON if specified
47902                    if self.match_text_seq(&["FORMAT", "JSON"]) {
47903                        exprs.push(Expression::JSONFormat(Box::new(JSONFormat {
47904                            this: Some(Box::new(kv)),
47905                            options: Vec::new(),
47906                            is_json: None,
47907                            to_json: None,
47908                        })));
47909                    } else {
47910                        exprs.push(kv);
47911                    }
47912                } else {
47913                    break;
47914                }
47915                if !self.match_token(TokenType::Comma) {
47916                    break;
47917                }
47918            }
47919            exprs
47920        };
47921
47922        // Parse NULL handling: NULL ON NULL or ABSENT ON NULL
47923        let null_handling = self.parse_json_on_null_handling()?;
47924
47925        // Parse UNIQUE KEYS option
47926        let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE"]) {
47927            self.match_text_seq(&["KEYS"]);
47928            Some(Box::new(Expression::Boolean(BooleanLiteral {
47929                value: true,
47930            })))
47931        } else if self.match_text_seq(&["WITHOUT", "UNIQUE"]) {
47932            self.match_text_seq(&["KEYS"]);
47933            Some(Box::new(Expression::Boolean(BooleanLiteral {
47934                value: false,
47935            })))
47936        } else {
47937            None
47938        };
47939
47940        // Consume optional KEYS keyword
47941        self.match_text_seq(&["KEYS"]);
47942
47943        // Parse RETURNING clause
47944        let return_type = if self.match_text_seq(&["RETURNING"]) {
47945            let type_expr = self.parse_type()?;
47946            // Wrap with FORMAT JSON if specified
47947            if self.match_text_seq(&["FORMAT", "JSON"]) {
47948                type_expr.map(|t| {
47949                    Box::new(Expression::JSONFormat(Box::new(JSONFormat {
47950                        this: Some(Box::new(t)),
47951                        options: Vec::new(),
47952                        is_json: None,
47953                        to_json: None,
47954                    })))
47955                })
47956            } else {
47957                type_expr.map(Box::new)
47958            }
47959        } else {
47960            None
47961        };
47962
47963        // Parse ENCODING option
47964        let encoding = if self.match_text_seq(&["ENCODING"]) {
47965            self.parse_var()?.map(Box::new)
47966        } else {
47967            None
47968        };
47969
47970        if agg {
47971            Ok(Some(Expression::JSONObjectAgg(Box::new(JSONObjectAgg {
47972                expressions,
47973                null_handling,
47974                unique_keys,
47975                return_type,
47976                encoding,
47977            }))))
47978        } else {
47979            Ok(Some(Expression::JSONObject(Box::new(JSONObject {
47980                expressions,
47981                null_handling,
47982                unique_keys,
47983                return_type,
47984                encoding,
47985            }))))
47986        }
47987    }
47988
47989    /// Parse JSON NULL handling clause: NULL ON NULL or ABSENT ON NULL
47990    fn parse_json_on_null_handling(&mut self) -> Result<Option<Box<Expression>>> {
47991        if self.match_text_seq(&["NULL", "ON", "NULL"]) {
47992            Ok(Some(Box::new(Expression::Var(Box::new(Var {
47993                this: "NULL ON NULL".to_string(),
47994            })))))
47995        } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
47996            Ok(Some(Box::new(Expression::Var(Box::new(Var {
47997                this: "ABSENT ON NULL".to_string(),
47998            })))))
47999        } else {
48000            Ok(None)
48001        }
48002    }
48003
48004    /// parse_json_schema - Implemented from Python _parse_json_schema
48005    #[allow(unused_variables, unused_mut)]
48006    pub fn parse_json_schema(&mut self) -> Result<Option<Expression>> {
48007        if self.match_text_seq(&["COLUMNS"]) {
48008            return Ok(Some(Expression::JSONSchema(Box::new(JSONSchema {
48009                expressions: Vec::new(),
48010            }))));
48011        }
48012        Ok(None)
48013    }
48014
48015    /// Parse JSON_TABLE COLUMNS clause: COLUMNS (column_def, column_def, ...) or COLUMNS column_def
48016    /// Column definitions can be:
48017    /// - name type PATH 'json_path'
48018    /// - name FOR ORDINALITY
48019    /// - NESTED [PATH] 'json_path' COLUMNS (...)
48020    pub fn parse_json_table_columns(&mut self) -> Result<Option<Expression>> {
48021        if !self.match_text_seq(&["COLUMNS"]) {
48022            return Ok(None);
48023        }
48024
48025        // Check for opening paren - Oracle supports both COLUMNS(...) and COLUMNS col PATH '...'
48026        let has_parens = self.match_token(TokenType::LParen);
48027
48028        let mut columns = Vec::new();
48029
48030        // Parse column definitions
48031        if has_parens {
48032            // COLUMNS(col1, col2, ...)
48033            if !self.check(TokenType::RParen) {
48034                loop {
48035                    if let Some(col_def) = self.parse_json_table_column_def()? {
48036                        columns.push(col_def);
48037                    }
48038                    if !self.match_token(TokenType::Comma) {
48039                        break;
48040                    }
48041                }
48042            }
48043            // Expect closing paren for COLUMNS(...)
48044            self.expect(TokenType::RParen)?;
48045        } else {
48046            // COLUMNS col PATH '...' (single column without parens)
48047            if let Some(col_def) = self.parse_json_table_column_def()? {
48048                columns.push(col_def);
48049            }
48050        }
48051
48052        Ok(Some(Expression::JSONSchema(Box::new(JSONSchema {
48053            expressions: columns,
48054        }))))
48055    }
48056
48057    /// Parse a single JSON_TABLE column definition
48058    /// Formats:
48059    /// - name [FOR ORDINALITY] [type] [PATH 'path']
48060    /// - NESTED [PATH] 'path' COLUMNS (...)
48061    pub fn parse_json_table_column_def(&mut self) -> Result<Option<Expression>> {
48062        // Check for NESTED column
48063        if self.match_text_seq(&["NESTED"]) {
48064            // NESTED [PATH] 'json_path' COLUMNS (...)
48065            self.match_text_seq(&["PATH"]); // Optional PATH keyword
48066            let path = self.parse_string()?;
48067            let nested_schema = self.parse_json_table_columns()?;
48068
48069            return Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
48070                this: None,
48071                kind: None,
48072                path: path.map(Box::new),
48073                nested_schema: nested_schema.map(Box::new),
48074                ordinality: None,
48075            }))));
48076        }
48077
48078        // Regular column: name [FOR ORDINALITY] [type] [PATH 'path']
48079        let name = self.parse_id_var()?;
48080        if name.is_none() {
48081            return Ok(None);
48082        }
48083
48084        // Check for FOR ORDINALITY
48085        let ordinality = if self.match_text_seq(&["FOR", "ORDINALITY"]) {
48086            Some(Box::new(Expression::Boolean(BooleanLiteral {
48087                value: true,
48088            })))
48089        } else {
48090            None
48091        };
48092
48093        // Parse data type (if not FOR ORDINALITY, type is expected)
48094        let kind = if ordinality.is_none() {
48095            // Try to parse a data type
48096            let data_type = self.parse_data_type_optional()?;
48097            data_type.map(|dt| self.data_type_to_string(&dt))
48098        } else {
48099            None
48100        };
48101
48102        // Parse PATH 'json_path'
48103        let path = if self.match_text_seq(&["PATH"]) {
48104            self.parse_string()?
48105        } else {
48106            None
48107        };
48108
48109        Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
48110            this: name.map(Box::new),
48111            kind,
48112            path: path.map(Box::new),
48113            nested_schema: None,
48114            ordinality,
48115        }))))
48116    }
48117
48118    /// Parse JSON_TABLE function
48119    /// JSON_TABLE(expr, path COLUMNS (...)) [ON ERROR ...] [ON EMPTY ...]
48120    pub fn parse_json_table(&mut self) -> Result<Option<Expression>> {
48121        // Parse the JSON expression
48122        let this = self.parse_expression()?;
48123
48124        // Optional path after comma
48125        let path = if self.match_token(TokenType::Comma) {
48126            if let Some(s) = self.parse_string()? {
48127                Some(Box::new(s))
48128            } else {
48129                None
48130            }
48131        } else {
48132            None
48133        };
48134
48135        // Parse error handling: ON ERROR NULL or ON ERROR ERROR
48136        let error_handling = if self.match_text_seq(&["ON", "ERROR"]) {
48137            if self.match_text_seq(&["NULL"]) {
48138                Some(Box::new(Expression::Var(Box::new(Var {
48139                    this: "NULL".to_string(),
48140                }))))
48141            } else if self.match_text_seq(&["ERROR"]) {
48142                Some(Box::new(Expression::Var(Box::new(Var {
48143                    this: "ERROR".to_string(),
48144                }))))
48145            } else {
48146                None
48147            }
48148        } else {
48149            None
48150        };
48151
48152        // Parse empty handling: ON EMPTY NULL or ON EMPTY ERROR
48153        let empty_handling = if self.match_text_seq(&["ON", "EMPTY"]) {
48154            if self.match_text_seq(&["NULL"]) {
48155                Some(Box::new(Expression::Var(Box::new(Var {
48156                    this: "NULL".to_string(),
48157                }))))
48158            } else if self.match_text_seq(&["ERROR"]) {
48159                Some(Box::new(Expression::Var(Box::new(Var {
48160                    this: "ERROR".to_string(),
48161                }))))
48162            } else {
48163                None
48164            }
48165        } else {
48166            None
48167        };
48168
48169        // Parse COLUMNS clause
48170        let schema = self.parse_json_schema()?;
48171
48172        Ok(Some(Expression::JSONTable(Box::new(JSONTable {
48173            this: Box::new(this),
48174            schema: schema.map(Box::new),
48175            path,
48176            error_handling,
48177            empty_handling,
48178        }))))
48179    }
48180
48181    /// parse_json_value - Ported from Python _parse_json_value
48182    #[allow(unused_variables, unused_mut)]
48183    /// parse_json_value - Parses JSON_VALUE function
48184    /// Example: JSON_VALUE(json, '$.path' RETURNING type)
48185    pub fn parse_json_value(&mut self) -> Result<Option<Expression>> {
48186        // Parse the JSON expression
48187        let this = self.parse_expression()?;
48188
48189        // Parse path (after comma)
48190        self.match_token(TokenType::Comma);
48191        let path = self.parse_expression()?;
48192
48193        // Parse optional RETURNING type
48194        let returning = if self.match_token(TokenType::Returning) {
48195            Some(Box::new(self.parse_expression()?))
48196        } else {
48197            None
48198        };
48199
48200        // Parse optional ON condition (ON ERROR, ON EMPTY)
48201        let on_condition = if self.check(TokenType::On) {
48202            self.parse_on_condition()?
48203        } else {
48204            None
48205        };
48206
48207        Ok(Some(Expression::JSONValue(Box::new(JSONValue {
48208            this: Box::new(this),
48209            path: Some(Box::new(path)),
48210            returning,
48211            on_condition: on_condition.map(Box::new),
48212        }))))
48213    }
48214
48215    /// parse_key_constraint_options - Implemented from Python _parse_key_constraint_options
48216    #[allow(unused_variables, unused_mut)]
48217    pub fn parse_key_constraint_options(&mut self) -> Result<Option<Expression>> {
48218        if self.match_text_seq(&["NO", "ACTION"]) {
48219            // Matched: NO ACTION
48220            return Ok(None);
48221        }
48222        if self.match_text_seq(&["CASCADE"]) {
48223            // Matched: CASCADE
48224            return Ok(None);
48225        }
48226        if self.match_text_seq(&["RESTRICT"]) {
48227            // Matched: RESTRICT
48228            return Ok(None);
48229        }
48230        Ok(None)
48231    }
48232
48233    /// parse_lambda - Ported from Python _parse_lambda
48234    /// Parses lambda expressions: x -> x + 1 or (x, y) -> x + y
48235    /// Also supports DuckDB syntax: LAMBDA x : x + 1
48236    #[allow(unused_variables, unused_mut)]
48237    pub fn parse_lambda(&mut self) -> Result<Option<Expression>> {
48238        let start_index = self.current;
48239
48240        // Check for DuckDB's LAMBDA keyword syntax: LAMBDA x : expr
48241        // ClickHouse doesn't use LAMBDA keyword — lambda is just a function name there
48242        if !matches!(
48243            self.config.dialect,
48244            Some(crate::dialects::DialectType::ClickHouse)
48245        ) && self.match_token(TokenType::Lambda)
48246        {
48247            // Parse lambda parameters (comma-separated identifiers)
48248            let mut params = Vec::new();
48249            loop {
48250                // Use is_identifier_token which handles Identifier, QuotedIdentifier, and Var
48251                if self.is_identifier_token() {
48252                    let token = self.advance();
48253                    let quoted = token.token_type == TokenType::QuotedIdentifier;
48254                    params.push(Identifier {
48255                        name: token.text,
48256                        quoted,
48257                        trailing_comments: Vec::new(),
48258                        span: None,
48259                    });
48260                } else {
48261                    break;
48262                }
48263                if !self.match_token(TokenType::Comma) {
48264                    break;
48265                }
48266            }
48267
48268            // Must have at least one parameter
48269            if params.is_empty() {
48270                return Err(self.parse_error("LAMBDA requires at least one parameter"));
48271            }
48272
48273            // Expect colon separator
48274            if !self.match_token(TokenType::Colon) {
48275                return Err(self.parse_error("Expected ':' after LAMBDA parameters"));
48276            }
48277
48278            let body = self.parse_expression()?;
48279            return Ok(Some(Expression::Lambda(Box::new(LambdaExpr {
48280                parameters: params,
48281                body,
48282                colon: true,
48283                parameter_types: Vec::new(),
48284            }))));
48285        }
48286
48287        // Try to parse lambda parameters
48288        let parameters = if self.match_token(TokenType::LParen) {
48289            // Parenthesized parameters: (x, y) -> ...
48290            let mut params = Vec::new();
48291            if !self.check(TokenType::RParen) {
48292                loop {
48293                    if let Some(ident) = self.parse_identifier()? {
48294                        if let Expression::Identifier(id) = ident {
48295                            params.push(id);
48296                        }
48297                    }
48298                    if !self.match_token(TokenType::Comma) {
48299                        break;
48300                    }
48301                }
48302            }
48303            if !self.match_token(TokenType::RParen) {
48304                // Not a lambda, retreat
48305                self.current = start_index;
48306                return Ok(None);
48307            }
48308            params
48309        } else {
48310            // Single parameter: x -> ...
48311            if let Some(ident) = self.parse_identifier()? {
48312                if let Expression::Identifier(id) = ident {
48313                    vec![id]
48314                } else {
48315                    self.current = start_index;
48316                    return Ok(None);
48317                }
48318            } else {
48319                return Ok(None);
48320            }
48321        };
48322
48323        // Check for arrow operator
48324        if self.match_token(TokenType::Arrow) || self.match_token(TokenType::FArrow) {
48325            // Parse lambda body
48326            let body = self.parse_expression()?;
48327            Ok(Some(Expression::Lambda(Box::new(LambdaExpr {
48328                parameters,
48329                body,
48330                colon: false,
48331                parameter_types: Vec::new(),
48332            }))))
48333        } else {
48334            // Not a lambda, retreat
48335            self.current = start_index;
48336            Ok(None)
48337        }
48338    }
48339
48340    /// parse_lambda_arg - Delegates to parse_id_var
48341    #[allow(unused_variables, unused_mut)]
48342    pub fn parse_lambda_arg(&mut self) -> Result<Option<Expression>> {
48343        self.parse_id_var()
48344    }
48345
48346    /// parse_lateral - Parse LATERAL subquery or table function
48347    /// Python: if self._match(TokenType.LATERAL): return exp.Lateral(this=..., view=..., outer=...)
48348    pub fn parse_lateral(&mut self) -> Result<Option<Expression>> {
48349        // Check for CROSS APPLY / OUTER APPLY (handled by join parsing in try_parse_join_kind)
48350        // This method focuses on LATERAL keyword parsing
48351
48352        if !self.match_token(TokenType::Lateral) {
48353            return Ok(None);
48354        }
48355
48356        // Check for LATERAL VIEW (Hive/Spark syntax)
48357        let view = self.match_token(TokenType::View);
48358        let outer = if view {
48359            self.match_token(TokenType::Outer)
48360        } else {
48361            false
48362        };
48363
48364        // Parse the lateral expression (subquery, function call, or table reference)
48365        let this = if self.check(TokenType::LParen) {
48366            // Could be a subquery: LATERAL (SELECT ...)
48367            self.expect(TokenType::LParen)?;
48368            let inner = self.parse_statement()?;
48369            self.expect(TokenType::RParen)?;
48370            inner
48371        } else {
48372            // Could be a function or table reference: LATERAL unnest(...)
48373            self.parse_primary()?
48374        };
48375
48376        // Parse optional alias
48377        let alias = if self.match_token(TokenType::As) {
48378            Some(self.expect_identifier()?)
48379        } else if self.check(TokenType::Identifier) && !self.check_keyword() {
48380            Some(self.expect_identifier()?)
48381        } else {
48382            None
48383        };
48384
48385        // Parse optional column aliases: AS alias(col1, col2, ...)
48386        let column_aliases = if alias.is_some() && self.match_token(TokenType::LParen) {
48387            let mut cols = Vec::new();
48388            loop {
48389                if self.check(TokenType::RParen) {
48390                    break;
48391                }
48392                let col = self.expect_identifier()?;
48393                cols.push(col);
48394                if !self.match_token(TokenType::Comma) {
48395                    break;
48396                }
48397            }
48398            self.expect(TokenType::RParen)?;
48399            cols
48400        } else {
48401            Vec::new()
48402        };
48403
48404        Ok(Some(Expression::Lateral(Box::new(Lateral {
48405            this: Box::new(this),
48406            view: if view {
48407                Some(Box::new(Expression::Boolean(BooleanLiteral {
48408                    value: true,
48409                })))
48410            } else {
48411                None
48412            },
48413            outer: if outer {
48414                Some(Box::new(Expression::Boolean(BooleanLiteral {
48415                    value: true,
48416                })))
48417            } else {
48418                None
48419            },
48420            alias,
48421            alias_quoted: false,
48422            cross_apply: None,
48423            ordinality: None,
48424            column_aliases,
48425        }))))
48426    }
48427
48428    /// parse_limit - Parse LIMIT clause
48429    /// Python: if self._match(TokenType.LIMIT): return exp.Limit(this=self._parse_term())
48430    pub fn parse_limit(&mut self) -> Result<Option<Expression>> {
48431        if !self.match_token(TokenType::Limit) {
48432            return Ok(None);
48433        }
48434        // Parse the limit expression (usually a number)
48435        let limit_expr = self.parse_expression()?;
48436        Ok(Some(Expression::Limit(Box::new(Limit {
48437            this: limit_expr,
48438            percent: false,
48439            comments: Vec::new(),
48440        }))))
48441    }
48442
48443    /// parse_limit_by - Implemented from Python _parse_limit_by
48444    #[allow(unused_variables, unused_mut)]
48445    pub fn parse_limit_by(&mut self) -> Result<Option<Expression>> {
48446        if self.match_text_seq(&["BY"]) {
48447            // Matched: BY
48448            return Ok(None);
48449        }
48450        Ok(None)
48451    }
48452
48453    /// parse_limit_options - Implemented from Python _parse_limit_options
48454    #[allow(unused_variables, unused_mut)]
48455    pub fn parse_limit_options(&mut self) -> Result<Option<Expression>> {
48456        if self.match_text_seq(&["ONLY"]) {
48457            return Ok(Some(Expression::LimitOptions(Box::new(LimitOptions {
48458                percent: None,
48459                rows: None,
48460                with_ties: None,
48461            }))));
48462        }
48463        if self.match_text_seq(&["WITH", "TIES"]) {
48464            // Matched: WITH TIES
48465            return Ok(None);
48466        }
48467        Ok(None)
48468    }
48469
48470    /// parse_load - Implemented from Python _parse_load
48471    #[allow(unused_variables, unused_mut)]
48472    pub fn parse_load(&mut self) -> Result<Option<Expression>> {
48473        if self.match_text_seq(&["DATA"]) {
48474            return Ok(Some(Expression::Command(Box::new(Command {
48475                this: String::new(),
48476            }))));
48477        }
48478        if self.match_text_seq(&["LOCAL"]) {
48479            // Matched: LOCAL
48480            return Ok(None);
48481        }
48482        Ok(None)
48483    }
48484
48485    /// parse_locking - Implemented from Python _parse_locking
48486    /// Calls: parse_table_parts
48487    #[allow(unused_variables, unused_mut)]
48488    pub fn parse_locking(&mut self) -> Result<Option<Expression>> {
48489        let kind = if self.match_token(TokenType::Table) {
48490            Some("TABLE")
48491        } else if self.match_token(TokenType::View) {
48492            Some("VIEW")
48493        } else if self.match_token(TokenType::Row) {
48494            Some("ROW")
48495        } else if self.match_token(TokenType::Database) || self.match_identifier("DATABASE") {
48496            Some("DATABASE")
48497        } else {
48498            None
48499        };
48500
48501        let kind = match kind {
48502            Some(k) => k.to_string(),
48503            None => return Ok(None),
48504        };
48505
48506        let this = if matches!(kind.as_str(), "DATABASE" | "TABLE" | "VIEW") {
48507            self.parse_table_parts()?
48508        } else {
48509            None
48510        };
48511
48512        let for_or_in = if self.match_token(TokenType::For) {
48513            Some("FOR")
48514        } else if self.match_token(TokenType::In) {
48515            Some("IN")
48516        } else {
48517            None
48518        };
48519
48520        let lock_type = if self.match_identifier("ACCESS") {
48521            Some("ACCESS")
48522        } else if self.match_texts(&["EXCL", "EXCLUSIVE"]) {
48523            Some("EXCLUSIVE")
48524        } else if self.match_identifier("SHARE") {
48525            Some("SHARE")
48526        } else if self.match_identifier("READ") {
48527            Some("READ")
48528        } else if self.match_identifier("WRITE") {
48529            Some("WRITE")
48530        } else if self.match_identifier("CHECKSUM") {
48531            Some("CHECKSUM")
48532        } else {
48533            None
48534        };
48535
48536        let override_ = if self.match_identifier("OVERRIDE") {
48537            Some(Box::new(Expression::Boolean(BooleanLiteral {
48538                value: true,
48539            })))
48540        } else {
48541            None
48542        };
48543
48544        Ok(Some(Expression::LockingProperty(Box::new(
48545            LockingProperty {
48546                this: this.map(Box::new),
48547                kind,
48548                for_or_in: for_or_in.map(|v| {
48549                    Box::new(Expression::Var(Box::new(Var {
48550                        this: v.to_string(),
48551                    })))
48552                }),
48553                lock_type: lock_type.map(|v| {
48554                    Box::new(Expression::Var(Box::new(Var {
48555                        this: v.to_string(),
48556                    })))
48557                }),
48558                override_,
48559            },
48560        ))))
48561    }
48562
48563    /// Parse Teradata LOCKING statement: LOCKING <property> SELECT ...
48564    fn parse_locking_statement(&mut self) -> Result<Expression> {
48565        self.expect(TokenType::Lock)?;
48566        let locking = self
48567            .parse_locking()?
48568            .ok_or_else(|| self.parse_error("Expected LOCKING clause"))?;
48569        let query = if self.check(TokenType::With) {
48570            self.parse_statement()?
48571        } else {
48572            self.parse_select()?
48573        };
48574        Ok(Expression::LockingStatement(Box::new(LockingStatement {
48575            this: Box::new(locking),
48576            expression: Box::new(query),
48577        })))
48578    }
48579
48580    /// parse_log - Parses LOG property (Teradata)
48581    /// Python: _parse_log
48582    /// Creates a LogProperty expression
48583    pub fn parse_log(&mut self) -> Result<Option<Expression>> {
48584        self.parse_log_impl(false)
48585    }
48586
48587    /// Implementation of parse_log with no flag
48588    pub fn parse_log_impl(&mut self, no: bool) -> Result<Option<Expression>> {
48589        Ok(Some(Expression::LogProperty(Box::new(LogProperty {
48590            no: if no {
48591                Some(Box::new(Expression::Boolean(BooleanLiteral {
48592                    value: true,
48593                })))
48594            } else {
48595                None
48596            },
48597        }))))
48598    }
48599
48600    /// parse_match_against - Parses MATCH(columns) AGAINST(pattern)
48601    /// Python: parser.py:7125-7153
48602    #[allow(unused_variables, unused_mut)]
48603    pub fn parse_match_against(&mut self) -> Result<Option<Expression>> {
48604        // Parse column expressions or TABLE syntax
48605        let expressions = if self.match_text_seq(&["TABLE"]) {
48606            // SingleStore TABLE syntax
48607            if let Some(table) = self.parse_table()? {
48608                vec![table]
48609            } else {
48610                Vec::new()
48611            }
48612        } else {
48613            // Regular column list
48614            let mut cols = Vec::new();
48615            loop {
48616                if let Some(col) = self.parse_column()? {
48617                    cols.push(col);
48618                }
48619                if !self.match_token(TokenType::Comma) {
48620                    break;
48621                }
48622            }
48623            cols
48624        };
48625
48626        // Match ) AGAINST (
48627        self.match_text_seq(&[")", "AGAINST", "("]);
48628
48629        // Parse the search pattern
48630        let this = self.parse_string()?;
48631
48632        // Parse modifier
48633        let modifier = if self.match_text_seq(&["IN", "NATURAL", "LANGUAGE", "MODE"]) {
48634            if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
48635                Some(Box::new(Expression::Var(Box::new(Var {
48636                    this: "IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION".to_string(),
48637                }))))
48638            } else {
48639                Some(Box::new(Expression::Var(Box::new(Var {
48640                    this: "IN NATURAL LANGUAGE MODE".to_string(),
48641                }))))
48642            }
48643        } else if self.match_text_seq(&["IN", "BOOLEAN", "MODE"]) {
48644            Some(Box::new(Expression::Var(Box::new(Var {
48645                this: "IN BOOLEAN MODE".to_string(),
48646            }))))
48647        } else if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
48648            Some(Box::new(Expression::Var(Box::new(Var {
48649                this: "WITH QUERY EXPANSION".to_string(),
48650            }))))
48651        } else {
48652            None
48653        };
48654
48655        match this {
48656            Some(t) => Ok(Some(Expression::MatchAgainst(Box::new(MatchAgainst {
48657                this: Box::new(t),
48658                expressions,
48659                modifier,
48660            })))),
48661            None => Ok(None),
48662        }
48663    }
48664
48665    /// parse_match_recognize_measure - Implemented from Python _parse_match_recognize_measure
48666    /// Parses a MEASURES expression in MATCH_RECOGNIZE: [FINAL|RUNNING] expression
48667    pub fn parse_match_recognize_measure(&mut self) -> Result<Option<Expression>> {
48668        // Check for optional FINAL or RUNNING keyword
48669        let window_frame = if self.match_texts(&["FINAL", "RUNNING"]) {
48670            let text = self.previous().text.to_ascii_uppercase();
48671            Some(if text == "FINAL" {
48672                MatchRecognizeSemantics::Final
48673            } else {
48674                MatchRecognizeSemantics::Running
48675            })
48676        } else {
48677            None
48678        };
48679
48680        // Parse the expression
48681        let this = self.parse_expression()?;
48682
48683        Ok(Some(Expression::MatchRecognizeMeasure(Box::new(
48684            MatchRecognizeMeasure { this, window_frame },
48685        ))))
48686    }
48687
48688    /// parse_max_min_by - MAX_BY / MIN_BY / ARG_MAX / ARG_MIN aggregate functions
48689    /// Parses: MAX_BY(value, key [, n]) or MIN_BY(value, key [, n])
48690    /// is_max: true for MAX_BY/ARG_MAX, false for MIN_BY/ARG_MIN
48691    #[allow(unused_variables, unused_mut)]
48692    pub fn parse_max_min_by(&mut self, is_max: bool) -> Result<Option<Expression>> {
48693        let mut args = Vec::new();
48694
48695        // Handle optional DISTINCT
48696        let distinct = if self.match_token(TokenType::Distinct) {
48697            let lambda_expr = self.parse_lambda()?;
48698            if let Some(expr) = lambda_expr {
48699                args.push(expr);
48700            }
48701            self.match_token(TokenType::Comma);
48702            true
48703        } else {
48704            false
48705        };
48706
48707        // Parse remaining arguments
48708        loop {
48709            if let Some(arg) = self.parse_lambda()? {
48710                args.push(arg);
48711            } else {
48712                break;
48713            }
48714            if !self.match_token(TokenType::Comma) {
48715                break;
48716            }
48717        }
48718
48719        let this = args
48720            .get(0)
48721            .cloned()
48722            .map(Box::new)
48723            .unwrap_or_else(|| Box::new(Expression::Null(Null)));
48724        let expression = args
48725            .get(1)
48726            .cloned()
48727            .map(Box::new)
48728            .unwrap_or_else(|| Box::new(Expression::Null(Null)));
48729        let count = args.get(2).cloned().map(Box::new);
48730
48731        if is_max {
48732            Ok(Some(Expression::ArgMax(Box::new(ArgMax {
48733                this,
48734                expression,
48735                count,
48736            }))))
48737        } else {
48738            Ok(Some(Expression::ArgMin(Box::new(ArgMin {
48739                this,
48740                expression,
48741                count,
48742            }))))
48743        }
48744    }
48745
48746    /// Parse MERGE statement
48747    /// Python: def _parse_merge(self) -> exp.Merge
48748    pub fn parse_merge(&mut self) -> Result<Option<Expression>> {
48749        // Optional INTO keyword
48750        self.match_token(TokenType::Into);
48751
48752        // Parse target table using parse_table_ref
48753        let mut target = Expression::Table(Box::new(self.parse_table_ref()?));
48754
48755        // Parse optional TSQL table hints: WITH (HOLDLOCK), WITH (TABLOCK), etc.
48756        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
48757            if let Expression::Table(ref mut table) = target {
48758                if let Some(hint_expr) = self.parse_table_hints()? {
48759                    match hint_expr {
48760                        Expression::Tuple(tuple) => {
48761                            table.hints = tuple.expressions;
48762                        }
48763                        other => {
48764                            table.hints = vec![other];
48765                        }
48766                    }
48767                }
48768            }
48769        }
48770
48771        // Parse optional alias for target table
48772        // Try to get an identifier as alias if AS is present or there's an identifier
48773        // Use parse_id_var instead of parse_identifier to handle Var tokens (e.g. T)
48774        if self.match_token(TokenType::As) {
48775            if let Some(alias_expr) = self.parse_id_var()? {
48776                // Extract identifier from the expression
48777                if let Expression::Identifier(ident) = alias_expr {
48778                    target = Expression::Alias(Box::new(Alias {
48779                        this: target,
48780                        alias: ident,
48781                        column_aliases: Vec::new(),
48782                        pre_alias_comments: Vec::new(),
48783                        trailing_comments: Vec::new(),
48784                        inferred_type: None,
48785                    }));
48786                }
48787            }
48788        } else if !self.check(TokenType::Using) {
48789            // Try to parse alias without AS keyword (e.g., MERGE t1 T USING ...)
48790            // Use parse_id_var to handle both Identifier and Var tokens
48791            if let Some(alias_expr) = self.parse_id_var()? {
48792                if let Expression::Identifier(ident) = alias_expr {
48793                    target = Expression::Alias(Box::new(Alias {
48794                        this: target,
48795                        alias: ident,
48796                        column_aliases: Vec::new(),
48797                        pre_alias_comments: Vec::new(),
48798                        trailing_comments: Vec::new(),
48799                        inferred_type: None,
48800                    }));
48801                }
48802            }
48803        }
48804
48805        // USING clause
48806        if !self.match_token(TokenType::Using) {
48807            return Err(self.parse_error("Expected USING in MERGE statement"));
48808        }
48809
48810        // Parse source table or subquery
48811        let mut using = if self.match_token(TokenType::LParen) {
48812            // Subquery: USING (SELECT ...) AS alias
48813            let query = self.parse_statement()?;
48814            self.expect(TokenType::RParen)?;
48815            let trailing = self.previous_trailing_comments().to_vec();
48816            let mut subq = Subquery {
48817                this: query,
48818                alias: None,
48819                column_aliases: Vec::new(),
48820                order_by: None,
48821                limit: None,
48822                offset: None,
48823                distribute_by: None,
48824                sort_by: None,
48825                cluster_by: None,
48826                lateral: false,
48827                modifiers_inside: false,
48828                trailing_comments: trailing,
48829                inferred_type: None,
48830            };
48831            // Parse optional alias: (SELECT ...) AS y(col1, col2)
48832            if self.match_token(TokenType::As) {
48833                let alias_name = self.expect_identifier_or_keyword()?;
48834                subq.alias = Some(Identifier::new(alias_name));
48835                // Parse optional column aliases: AS alias(col1, col2)
48836                if self.match_token(TokenType::LParen) {
48837                    let mut cols = Vec::new();
48838                    loop {
48839                        let col_name = self.expect_identifier_or_keyword()?;
48840                        cols.push(Identifier::new(col_name));
48841                        if !self.match_token(TokenType::Comma) {
48842                            break;
48843                        }
48844                    }
48845                    self.expect(TokenType::RParen)?;
48846                    subq.column_aliases = cols;
48847                }
48848            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
48849                // Implicit alias without AS
48850                let alias_name = self.expect_identifier_or_keyword()?;
48851                subq.alias = Some(Identifier::new(alias_name));
48852                // Parse optional column aliases: alias(col1, col2)
48853                if self.match_token(TokenType::LParen) {
48854                    let mut cols = Vec::new();
48855                    loop {
48856                        let col_name = self.expect_identifier_or_keyword()?;
48857                        cols.push(Identifier::new(col_name));
48858                        if !self.match_token(TokenType::Comma) {
48859                            break;
48860                        }
48861                    }
48862                    self.expect(TokenType::RParen)?;
48863                    subq.column_aliases = cols;
48864                }
48865            }
48866            Expression::Subquery(Box::new(subq))
48867        } else {
48868            Expression::Table(Box::new(self.parse_table_ref()?))
48869        };
48870
48871        // Parse optional alias for source (if not already parsed for subquery)
48872        if matches!(&using, Expression::Table(_)) {
48873            if self.match_token(TokenType::As) {
48874                if let Some(alias_expr) = self.parse_id_var()? {
48875                    if let Expression::Identifier(ident) = alias_expr {
48876                        using = Expression::Alias(Box::new(Alias {
48877                            this: using,
48878                            alias: ident,
48879                            column_aliases: Vec::new(),
48880                            pre_alias_comments: Vec::new(),
48881                            trailing_comments: Vec::new(),
48882                            inferred_type: None,
48883                        }));
48884                    }
48885                }
48886            } else if !self.check(TokenType::On) {
48887                // Try to parse alias without AS keyword
48888                // Use parse_id_var to handle both Identifier and Var tokens (e.g., S, T)
48889                if let Some(alias_expr) = self.parse_id_var()? {
48890                    if let Expression::Identifier(ident) = alias_expr {
48891                        using = Expression::Alias(Box::new(Alias {
48892                            this: using,
48893                            alias: ident,
48894                            column_aliases: Vec::new(),
48895                            pre_alias_comments: Vec::new(),
48896                            trailing_comments: Vec::new(),
48897                            inferred_type: None,
48898                        }));
48899                    }
48900                }
48901            }
48902        }
48903
48904        // ON clause with condition
48905        let on = if self.match_token(TokenType::On) {
48906            Some(Box::new(self.parse_expression()?))
48907        } else {
48908            None
48909        };
48910
48911        // Optional additional USING clause for key columns (DuckDB: USING (col1, col2))
48912        let using_cond = if self.match_token(TokenType::Using) {
48913            // Parse comma-separated identifiers wrapped in parentheses
48914            if self.match_token(TokenType::LParen) {
48915                let mut idents = Vec::new();
48916                loop {
48917                    // Use parse_id_var to handle Var tokens (unquoted identifiers)
48918                    if let Some(ident) = self.parse_id_var()? {
48919                        idents.push(ident);
48920                    } else {
48921                        break;
48922                    }
48923                    if !self.match_token(TokenType::Comma) {
48924                        break;
48925                    }
48926                }
48927                self.match_token(TokenType::RParen);
48928                if !idents.is_empty() {
48929                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
48930                        expressions: idents,
48931                    }))))
48932                } else {
48933                    None
48934                }
48935            } else {
48936                // Also support without parentheses for backwards compatibility
48937                let mut idents = Vec::new();
48938                loop {
48939                    if let Some(ident) = self.parse_id_var()? {
48940                        idents.push(ident);
48941                    } else {
48942                        break;
48943                    }
48944                    if !self.match_token(TokenType::Comma) {
48945                        break;
48946                    }
48947                }
48948                if !idents.is_empty() {
48949                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
48950                        expressions: idents,
48951                    }))))
48952                } else {
48953                    None
48954                }
48955            }
48956        } else {
48957            None
48958        };
48959
48960        // Parse WHEN MATCHED clauses
48961        let whens = self.parse_when_matched_clauses()?;
48962
48963        // Parse optional RETURNING clause (PostgreSQL) or OUTPUT clause (TSQL)
48964        let returning = if let Some(ret) = self.parse_returning()? {
48965            Some(ret)
48966        } else if self.match_token(TokenType::Output) {
48967            // TSQL OUTPUT clause: OUTPUT $action, Inserted.col, Deleted.col [INTO target]
48968            let output = self.parse_output_clause()?;
48969            Some(Expression::Returning(Box::new(Returning {
48970                expressions: output.columns,
48971                into: output.into_table.map(Box::new),
48972            })))
48973        } else {
48974            None
48975        };
48976
48977        Ok(Some(Expression::Merge(Box::new(Merge {
48978            this: Box::new(target),
48979            using: Box::new(using),
48980            on,
48981            using_cond,
48982            whens: whens.map(Box::new),
48983            with_: None,
48984            returning: returning.map(Box::new),
48985        }))))
48986    }
48987
48988    /// Parse multiple WHEN [NOT] MATCHED clauses for MERGE
48989    fn parse_when_matched_clauses(&mut self) -> Result<Option<Expression>> {
48990        let mut whens = Vec::new();
48991
48992        while self.match_token(TokenType::When) {
48993            // Check for NOT MATCHED
48994            let matched = !self.match_token(TokenType::Not);
48995            self.match_text_seq(&["MATCHED"]);
48996
48997            // Check for BY TARGET or BY SOURCE
48998            let source = if self.match_text_seq(&["BY", "TARGET"]) {
48999                Some(Box::new(Expression::Boolean(BooleanLiteral {
49000                    value: false,
49001                })))
49002            } else if self.match_text_seq(&["BY", "SOURCE"]) {
49003                Some(Box::new(Expression::Boolean(BooleanLiteral {
49004                    value: true,
49005                })))
49006            } else {
49007                None
49008            };
49009
49010            // Optional AND condition
49011            let condition = if self.match_token(TokenType::And) {
49012                Some(Box::new(self.parse_expression()?))
49013            } else {
49014                None
49015            };
49016
49017            // THEN action
49018            if !self.match_token(TokenType::Then) {
49019                return Err(self.parse_error("Expected THEN in WHEN clause"));
49020            }
49021
49022            // Parse the action: INSERT, UPDATE, DELETE, or other keywords (DO NOTHING, etc.)
49023            let then: Expression = if self.match_token(TokenType::Insert) {
49024                // INSERT action - use Tuple to represent it
49025                let mut elements = vec![Expression::Var(Box::new(Var {
49026                    this: "INSERT".to_string(),
49027                }))];
49028
49029                // Spark/Databricks: INSERT * (insert all columns)
49030                if self.match_token(TokenType::Star) {
49031                    elements.push(Expression::Star(crate::expressions::Star {
49032                        table: None,
49033                        except: None,
49034                        replace: None,
49035                        rename: None,
49036                        trailing_comments: Vec::new(),
49037                        span: None,
49038                    }));
49039                } else
49040                // Parse column list (optional)
49041                if self.match_token(TokenType::LParen) {
49042                    let mut columns: Vec<Expression> = Vec::new();
49043                    loop {
49044                        if let Some(col) = self.parse_id_var()? {
49045                            // Handle qualified column references (e.g., target.a)
49046                            let col = if self.match_token(TokenType::Dot) {
49047                                if let Expression::Identifier(table_ident) = col {
49048                                    if let Some(col_expr) = self.parse_id_var()? {
49049                                        if let Expression::Identifier(col_ident) = col_expr {
49050                                            Expression::boxed_column(Column {
49051                                                name: col_ident,
49052                                                table: Some(table_ident),
49053                                                join_mark: false,
49054                                                trailing_comments: Vec::new(),
49055                                                span: None,
49056                                                inferred_type: None,
49057                                            })
49058                                        } else {
49059                                            col_expr
49060                                        }
49061                                    } else {
49062                                        return Err(self.parse_error(
49063                                            "Expected column name after dot in MERGE INSERT",
49064                                        ));
49065                                    }
49066                                } else {
49067                                    col
49068                                }
49069                            } else {
49070                                col
49071                            };
49072                            columns.push(col);
49073                        } else {
49074                            break;
49075                        }
49076                        if !self.match_token(TokenType::Comma) {
49077                            break;
49078                        }
49079                    }
49080                    self.match_token(TokenType::RParen);
49081                    if !columns.is_empty() {
49082                        elements.push(Expression::Tuple(Box::new(Tuple {
49083                            expressions: columns,
49084                        })));
49085                    }
49086                }
49087
49088                // Parse VALUES clause
49089                if self.match_text_seq(&["VALUES"]) {
49090                    if let Some(values) = self.parse_value()? {
49091                        elements.push(values);
49092                    }
49093                } else if self.match_text_seq(&["ROW"]) {
49094                    elements.push(Expression::Var(Box::new(Var {
49095                        this: "ROW".to_string(),
49096                    })));
49097                }
49098
49099                if elements.len() == 1 {
49100                    elements[0].clone()
49101                } else {
49102                    Expression::Tuple(Box::new(Tuple {
49103                        expressions: elements,
49104                    }))
49105                }
49106            } else if self.match_token(TokenType::Update) {
49107                // UPDATE action - use Tuple to represent SET assignments
49108                let mut elements = vec![Expression::Var(Box::new(Var {
49109                    this: "UPDATE".to_string(),
49110                }))];
49111
49112                // Spark/Databricks: UPDATE * (update all columns)
49113                if self.match_token(TokenType::Star) {
49114                    elements.push(Expression::Star(crate::expressions::Star {
49115                        table: None,
49116                        except: None,
49117                        replace: None,
49118                        rename: None,
49119                        trailing_comments: Vec::new(),
49120                        span: None,
49121                    }));
49122                } else if self.match_token(TokenType::Set) {
49123                    // Parse col = value assignments manually
49124                    let mut assignments: Vec<Expression> = Vec::new();
49125                    loop {
49126                        // Parse: column = expression (column can be qualified like x.a)
49127                        if let Some(col) = self.parse_id_var()? {
49128                            // Handle qualified column references (e.g., x.a = y.b)
49129                            let col = if self.match_token(TokenType::Dot) {
49130                                // We have a qualified column reference
49131                                if let Expression::Identifier(table_ident) = col {
49132                                    // Parse the column part after the dot
49133                                    if let Some(col_expr) = self.parse_id_var()? {
49134                                        if let Expression::Identifier(col_ident) = col_expr {
49135                                            Expression::boxed_column(Column {
49136                                                name: col_ident,
49137                                                table: Some(table_ident),
49138                                                join_mark: false,
49139                                                trailing_comments: Vec::new(),
49140                                                span: None,
49141                                                inferred_type: None,
49142                                            })
49143                                        } else {
49144                                            col_expr
49145                                        }
49146                                    } else {
49147                                        return Err(
49148                                            self.parse_error("Expected column name after dot")
49149                                        );
49150                                    }
49151                                } else {
49152                                    col
49153                                }
49154                            } else {
49155                                col
49156                            };
49157                            if self.match_token(TokenType::Eq) {
49158                                let value = self.parse_expression()?;
49159                                // Create assignment as EQ expression
49160                                let assignment = Expression::Eq(Box::new(BinaryOp {
49161                                    left: col,
49162                                    right: value,
49163                                    left_comments: Vec::new(),
49164                                    operator_comments: Vec::new(),
49165                                    trailing_comments: Vec::new(),
49166                                    inferred_type: None,
49167                                }));
49168                                assignments.push(assignment);
49169                            }
49170                        }
49171                        if !self.match_token(TokenType::Comma) {
49172                            break;
49173                        }
49174                    }
49175                    if !assignments.is_empty() {
49176                        elements.push(Expression::Tuple(Box::new(Tuple {
49177                            expressions: assignments,
49178                        })));
49179                    }
49180                }
49181
49182                if elements.len() == 1 {
49183                    elements[0].clone()
49184                } else {
49185                    Expression::Tuple(Box::new(Tuple {
49186                        expressions: elements,
49187                    }))
49188                }
49189            } else if self.match_token(TokenType::Delete) {
49190                // DELETE action
49191                Expression::Var(Box::new(Var {
49192                    this: "DELETE".to_string(),
49193                }))
49194            } else if self.match_identifier("DO") {
49195                // DO NOTHING action (PostgreSQL)
49196                if self.match_identifier("NOTHING") {
49197                    Expression::Var(Box::new(Var {
49198                        this: "DO NOTHING".to_string(),
49199                    }))
49200                } else {
49201                    return Err(self.parse_error("Expected NOTHING after DO"));
49202                }
49203            } else {
49204                // Other action
49205                if let Some(var) = self.parse_var()? {
49206                    var
49207                } else {
49208                    return Err(
49209                        self.parse_error("Expected INSERT, UPDATE, DELETE, or action keyword")
49210                    );
49211                }
49212            };
49213
49214            whens.push(Expression::When(Box::new(When {
49215                matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
49216                    value: matched,
49217                }))),
49218                source,
49219                condition,
49220                then: Box::new(then),
49221            })));
49222        }
49223
49224        if whens.is_empty() {
49225            Ok(None)
49226        } else {
49227            Ok(Some(Expression::Whens(Box::new(Whens {
49228                expressions: whens,
49229            }))))
49230        }
49231    }
49232
49233    /// parse_mergeblockratio - Parses MERGEBLOCKRATIO property (Teradata)
49234    /// Python: _parse_mergeblockratio
49235    /// Format: MERGEBLOCKRATIO = number [PERCENT] or NO MERGEBLOCKRATIO or DEFAULT MERGEBLOCKRATIO
49236    pub fn parse_mergeblockratio(&mut self) -> Result<Option<Expression>> {
49237        self.parse_mergeblockratio_impl(false, false)
49238    }
49239
49240    /// Implementation of parse_mergeblockratio with options
49241    pub fn parse_mergeblockratio_impl(
49242        &mut self,
49243        no: bool,
49244        default: bool,
49245    ) -> Result<Option<Expression>> {
49246        // Check for = followed by a number
49247        if self.match_token(TokenType::Eq) {
49248            let this = self.parse_number()?;
49249            let percent = self.match_token(TokenType::Percent);
49250
49251            Ok(Some(Expression::MergeBlockRatioProperty(Box::new(
49252                MergeBlockRatioProperty {
49253                    this: this.map(Box::new),
49254                    no: None,
49255                    default: None,
49256                    percent: if percent {
49257                        Some(Box::new(Expression::Boolean(BooleanLiteral {
49258                            value: true,
49259                        })))
49260                    } else {
49261                        None
49262                    },
49263                },
49264            ))))
49265        } else {
49266            // NO or DEFAULT variant
49267            Ok(Some(Expression::MergeBlockRatioProperty(Box::new(
49268                MergeBlockRatioProperty {
49269                    this: None,
49270                    no: if no {
49271                        Some(Box::new(Expression::Boolean(BooleanLiteral {
49272                            value: true,
49273                        })))
49274                    } else {
49275                        None
49276                    },
49277                    default: if default {
49278                        Some(Box::new(Expression::Boolean(BooleanLiteral {
49279                            value: true,
49280                        })))
49281                    } else {
49282                        None
49283                    },
49284                    percent: None,
49285                },
49286            ))))
49287        }
49288    }
49289
49290    /// parse_modifies_property - Implemented from Python _parse_modifies_property
49291    #[allow(unused_variables, unused_mut)]
49292    pub fn parse_modifies_property(&mut self) -> Result<Option<Expression>> {
49293        if self.match_text_seq(&["SQL", "DATA"]) {
49294            // Matched: SQL DATA
49295            return Ok(None);
49296        }
49297        Ok(None)
49298    }
49299
49300    /// parse_multitable_inserts - Parses Oracle's multi-table INSERT (INSERT ALL/FIRST)
49301    /// Python: _parse_multitable_inserts
49302    /// Syntax: INSERT ALL|FIRST [WHEN cond THEN] INTO table [(cols)] [VALUES(...)] ... SELECT ...
49303    pub fn parse_multitable_inserts(
49304        &mut self,
49305        leading_comments: Vec<String>,
49306    ) -> Result<Option<Expression>> {
49307        // Get kind from previous token (ALL or FIRST)
49308        let kind = self.previous().text.to_ascii_uppercase();
49309
49310        let mut expressions = Vec::new();
49311
49312        // Helper closure to parse a single conditional insert
49313        // Returns None when no more INTO clauses found
49314        loop {
49315            // Check for WHEN condition
49316            let condition = if self.match_token(TokenType::When) {
49317                let cond = self.parse_or()?;
49318                self.match_token(TokenType::Then);
49319                Some(cond)
49320            } else {
49321                None
49322            };
49323
49324            // Check for ELSE (used in INSERT FIRST ... ELSE INTO ...)
49325            let is_else = self.match_token(TokenType::Else);
49326
49327            // Must have INTO keyword to continue
49328            if !self.match_token(TokenType::Into) {
49329                break;
49330            }
49331
49332            // Parse table with optional schema (using parse_table_parts for proper schema.table parsing)
49333            let table_expr = self.parse_table_parts()?;
49334
49335            // Extract TableRef from the table expression
49336            let table_ref = if let Some(Expression::Table(t)) = table_expr {
49337                *t
49338            } else {
49339                // Fallback: create empty table ref (shouldn't happen)
49340                TableRef::new("")
49341            };
49342
49343            // Parse optional column list: (col1, col2, ...)
49344            let columns = if self.match_token(TokenType::LParen) {
49345                let cols = self.parse_identifier_list()?;
49346                self.expect(TokenType::RParen)?;
49347                cols
49348            } else {
49349                Vec::new()
49350            };
49351
49352            // Parse optional VALUES clause
49353            let values = if self.match_token(TokenType::Values) {
49354                self.expect(TokenType::LParen)?;
49355                let row = self.parse_expression_list()?;
49356                self.expect(TokenType::RParen)?;
49357                vec![row]
49358            } else {
49359                Vec::new()
49360            };
49361
49362            // Create Insert expression for this INTO clause
49363            let insert_expr = Expression::Insert(Box::new(Insert {
49364                table: table_ref,
49365                columns,
49366                values,
49367                query: None,
49368                overwrite: false,
49369                partition: Vec::new(),
49370                directory: None,
49371                returning: Vec::new(),
49372                output: None,
49373                on_conflict: None,
49374                leading_comments: Vec::new(),
49375                if_exists: false,
49376                with: None,
49377                ignore: false,
49378                source_alias: None,
49379                alias: None,
49380                alias_explicit_as: false,
49381                default_values: false,
49382                by_name: false,
49383                conflict_action: None,
49384                is_replace: false,
49385                replace_where: None,
49386                source: None,
49387                hint: None,
49388                function_target: None,
49389                partition_by: None,
49390                settings: Vec::new(),
49391            }));
49392
49393            // Wrap in ConditionalInsert
49394            let conditional_insert = Expression::ConditionalInsert(Box::new(ConditionalInsert {
49395                this: Box::new(insert_expr),
49396                expression: condition.map(Box::new),
49397                else_: if is_else {
49398                    Some(Box::new(Expression::Boolean(BooleanLiteral {
49399                        value: true,
49400                    })))
49401                } else {
49402                    None
49403                },
49404            }));
49405
49406            expressions.push(conditional_insert);
49407        }
49408
49409        // Parse the source SELECT statement (or subquery)
49410        let source = self.parse_statement()?;
49411
49412        Ok(Some(Expression::MultitableInserts(Box::new(
49413            MultitableInserts {
49414                kind,
49415                expressions,
49416                source: Some(Box::new(source)),
49417                leading_comments,
49418            },
49419        ))))
49420    }
49421
49422    /// parse_name_as_expression - Parse identifier that can be aliased
49423    /// Parses: identifier [AS expression]
49424    #[allow(unused_variables, unused_mut)]
49425    pub fn parse_name_as_expression(&mut self) -> Result<Option<Expression>> {
49426        // Parse the identifier
49427        let this = self.parse_id_var()?;
49428        if this.is_none() {
49429            return Ok(None);
49430        }
49431
49432        // Check for AS alias
49433        if self.match_token(TokenType::Alias) {
49434            let expression = self.parse_disjunction()?;
49435            if expression.is_none() {
49436                return Ok(this);
49437            }
49438
49439            // Extract the identifier for the alias
49440            let alias_ident =
49441                match this.ok_or_else(|| self.parse_error("Expected identifier for alias"))? {
49442                    Expression::Identifier(id) => id,
49443                    _ => Identifier::new(String::new()),
49444                };
49445
49446            return Ok(Some(Expression::Alias(Box::new(Alias {
49447                this: expression.ok_or_else(|| self.parse_error("Expected expression after AS"))?,
49448                alias: alias_ident,
49449                column_aliases: Vec::new(),
49450                pre_alias_comments: Vec::new(),
49451                trailing_comments: Vec::new(),
49452                inferred_type: None,
49453            }))));
49454        }
49455
49456        Ok(this)
49457    }
49458
49459    /// parse_named_window - Ported from Python _parse_named_window
49460    /// Parses a named window definition: name AS (spec)
49461    #[allow(unused_variables, unused_mut)]
49462    pub fn parse_named_window(&mut self) -> Result<Option<Expression>> {
49463        // Parse window name
49464        let name = self.parse_id_var()?;
49465        if name.is_none() {
49466            return Ok(None);
49467        }
49468
49469        // Expect AS
49470        if !self.match_token(TokenType::As) {
49471            return Ok(name); // Just the name, no spec
49472        }
49473
49474        // Parse window spec (parenthesized)
49475        self.expect(TokenType::LParen)?;
49476        let spec = self.parse_window_spec_inner()?;
49477        self.expect(TokenType::RParen)?;
49478
49479        if let (Some(name_expr), Some(spec_expr)) = (name, spec) {
49480            // Create an Alias expression wrapping the spec with the name
49481            let alias_ident = if let Expression::Identifier(id) = name_expr {
49482                id
49483            } else {
49484                Identifier::new("window")
49485            };
49486            Ok(Some(Expression::Alias(Box::new(Alias {
49487                this: spec_expr,
49488                alias: alias_ident,
49489                column_aliases: Vec::new(),
49490                pre_alias_comments: Vec::new(),
49491                trailing_comments: Vec::new(),
49492                inferred_type: None,
49493            }))))
49494        } else {
49495            Ok(None)
49496        }
49497    }
49498
49499    /// parse_next_value_for - Parses NEXT VALUE FOR sequence_name
49500    /// Python: parser.py:6752-6761
49501    #[allow(unused_variables, unused_mut)]
49502    pub fn parse_next_value_for(&mut self) -> Result<Option<Expression>> {
49503        if !self.match_text_seq(&["VALUE", "FOR"]) {
49504            // Retreat if we consumed a token
49505            if self.current > 0 {
49506                self.current -= 1;
49507            }
49508            return Ok(None);
49509        }
49510
49511        // Parse the sequence name as a dotted identifier (db.schema.sequence_name)
49512        // Manually parse identifier parts separated by dots
49513        let first = self
49514            .parse_id_var()?
49515            .ok_or_else(|| self.parse_error("Expected sequence name after NEXT VALUE FOR"))?;
49516        let first_id = match first {
49517            Expression::Identifier(id) => id,
49518            Expression::Var(v) => Identifier {
49519                name: v.this,
49520                quoted: false,
49521                trailing_comments: Vec::new(),
49522                span: None,
49523            },
49524            _ => Identifier {
49525                name: String::new(),
49526                quoted: false,
49527                trailing_comments: Vec::new(),
49528                span: None,
49529            },
49530        };
49531
49532        // Check for dotted parts (db.schema.sequence_name)
49533        let mut parts = vec![first_id];
49534        while self.match_token(TokenType::Dot) {
49535            if self.is_identifier_or_keyword_token() {
49536                let token = self.advance();
49537                parts.push(Identifier {
49538                    name: token.text,
49539                    quoted: token.token_type == TokenType::QuotedIdentifier,
49540                    trailing_comments: Vec::new(),
49541                    span: None,
49542                });
49543            } else {
49544                break;
49545            }
49546        }
49547
49548        // Build a Column expression from the parts
49549        let this = if parts.len() == 1 {
49550            Expression::boxed_column(Column {
49551                name: parts.remove(0),
49552                table: None,
49553                join_mark: false,
49554                trailing_comments: Vec::new(),
49555                span: None,
49556                inferred_type: None,
49557            })
49558        } else if parts.len() == 2 {
49559            Expression::boxed_column(Column {
49560                name: parts.remove(1),
49561                table: Some(parts.remove(0)),
49562                join_mark: false,
49563                trailing_comments: Vec::new(),
49564                span: None,
49565                inferred_type: None,
49566            })
49567        } else {
49568            // For 3+ parts, build nested Dot expressions
49569            let mut expr = Expression::Identifier(parts.remove(0));
49570            for part in parts.drain(..) {
49571                expr = Expression::Dot(Box::new(DotAccess {
49572                    this: expr,
49573                    field: part,
49574                }));
49575            }
49576            expr
49577        };
49578
49579        // Parse optional OVER (ORDER BY ...) clause
49580        let order = if self.match_token(TokenType::Over) {
49581            if self.match_token(TokenType::LParen) {
49582                let ord = self.parse_order()?;
49583                self.expect(TokenType::RParen)?;
49584                ord.map(Box::new)
49585            } else {
49586                None
49587            }
49588        } else {
49589            None
49590        };
49591
49592        Ok(Some(Expression::NextValueFor(Box::new(NextValueFor {
49593            this: Box::new(this),
49594            order,
49595        }))))
49596    }
49597
49598    /// parse_no_property - Implemented from Python _parse_no_property
49599    #[allow(unused_variables, unused_mut)]
49600    pub fn parse_no_property(&mut self) -> Result<Option<Expression>> {
49601        if self.match_text_seq(&["PRIMARY", "INDEX"]) {
49602            // Matched: PRIMARY INDEX
49603            return Ok(None);
49604        }
49605        if self.match_text_seq(&["SQL"]) {
49606            // Matched: SQL
49607            return Ok(None);
49608        }
49609        Ok(None)
49610    }
49611
49612    /// parse_normalize - Ported from Python _parse_normalize
49613    #[allow(unused_variables, unused_mut)]
49614    /// parse_normalize - Parses NORMALIZE(expr [, form])
49615    /// Python: NORMALIZE(expr, form) where form is NFC/NFD/NFKC/NFKD
49616    pub fn parse_normalize(&mut self) -> Result<Option<Expression>> {
49617        // Parse the expression to normalize
49618        let this = self.parse_expression()?;
49619
49620        // Check for optional form argument
49621        let form = if self.match_token(TokenType::Comma) {
49622            self.parse_var()?.map(Box::new)
49623        } else {
49624            None
49625        };
49626
49627        Ok(Some(Expression::Normalize(Box::new(Normalize {
49628            this: Box::new(this),
49629            form,
49630            is_casefold: None,
49631        }))))
49632    }
49633
49634    /// parse_not_constraint - Implemented from Python _parse_not_constraint
49635    /// Parses constraints that start with NOT: NOT NULL, NOT CASESPECIFIC
49636    pub fn parse_not_constraint(&mut self) -> Result<Option<Expression>> {
49637        // NOT NULL constraint
49638        if self.match_text_seq(&["NULL"]) {
49639            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
49640                NotNullColumnConstraint { allow_null: None },
49641            ))));
49642        }
49643        // NOT CASESPECIFIC constraint (Teradata)
49644        if self.match_text_seq(&["CASESPECIFIC"]) {
49645            return Ok(Some(Expression::CaseSpecificColumnConstraint(Box::new(
49646                CaseSpecificColumnConstraint {
49647                    not_: Some(Box::new(Expression::Boolean(BooleanLiteral {
49648                        value: true,
49649                    }))),
49650                },
49651            ))));
49652        }
49653        // NOT FOR REPLICATION (SQL Server) - consume the tokens and return as a property
49654        if self.match_token(TokenType::For) && self.match_identifier("REPLICATION") {
49655            return Ok(Some(Expression::Property(Box::new(
49656                crate::expressions::Property {
49657                    this: Box::new(Expression::Identifier(Identifier::new(
49658                        "NOT FOR REPLICATION".to_string(),
49659                    ))),
49660                    value: None,
49661                },
49662            ))));
49663        }
49664        Ok(None)
49665    }
49666
49667    /// parse_null - Parse NULL literal
49668    /// Python: if self._match_set((TokenType.NULL, TokenType.UNKNOWN)): return exp.Null
49669    pub fn parse_null(&mut self) -> Result<Option<Expression>> {
49670        if self.match_token(TokenType::Null) {
49671            return Ok(Some(Expression::Null(Null)));
49672        }
49673        // UNKNOWN is treated as NULL in some dialects
49674        if self.match_token(TokenType::Unknown) {
49675            return Ok(Some(Expression::Null(Null)));
49676        }
49677        Ok(None)
49678    }
49679
49680    /// parse_number - Parse numeric literal
49681    /// Python: TokenType.NUMBER -> exp.Literal(this=token.text, is_string=False)
49682    /// Handles Hive/Spark numeric suffixes encoded as "number::TYPE" by the tokenizer
49683    pub fn parse_number(&mut self) -> Result<Option<Expression>> {
49684        if self.match_token(TokenType::Number) {
49685            let text = self.previous().text.clone();
49686            // Check for numeric literal suffix encoded as "number::TYPE"
49687            if let Some(sep_pos) = text.find("::") {
49688                let num_part = &text[..sep_pos];
49689                let type_name = &text[sep_pos + 2..];
49690                // Create a TryCast expression: TRY_CAST(number AS TYPE)
49691                let num_expr = Expression::Literal(Box::new(Literal::Number(num_part.to_string())));
49692                let data_type = match type_name {
49693                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
49694                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
49695                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
49696                    "DOUBLE" => crate::expressions::DataType::Double {
49697                        precision: None,
49698                        scale: None,
49699                    },
49700                    "FLOAT" => crate::expressions::DataType::Float {
49701                        precision: None,
49702                        scale: None,
49703                        real_spelling: false,
49704                    },
49705                    "DECIMAL" => crate::expressions::DataType::Decimal {
49706                        precision: None,
49707                        scale: None,
49708                    },
49709                    _ => crate::expressions::DataType::Custom {
49710                        name: type_name.to_string(),
49711                    },
49712                };
49713                return Ok(Some(Expression::TryCast(Box::new(
49714                    crate::expressions::Cast {
49715                        this: num_expr,
49716                        to: data_type,
49717                        trailing_comments: Vec::new(),
49718                        double_colon_syntax: false,
49719                        format: None,
49720                        default: None,
49721                        inferred_type: None,
49722                    },
49723                ))));
49724            }
49725            return Ok(Some(Expression::Literal(Box::new(Literal::Number(text)))));
49726        }
49727        Ok(None)
49728    }
49729
49730    /// parse_odbc_datetime_literal - Ported from Python _parse_odbc_datetime_literal
49731    #[allow(unused_variables, unused_mut)]
49732    /// parse_odbc_datetime_literal - Parses ODBC datetime literals
49733    /// Examples: {d'2023-01-01'}, {t'12:00:00'}, {ts'2023-01-01 12:00:00'}
49734    pub fn parse_odbc_datetime_literal(&mut self) -> Result<Option<Expression>> {
49735        // Match the type indicator (d, t, ts)
49736        if !self.match_token(TokenType::Var) {
49737            return Ok(None);
49738        }
49739        let type_indicator = self.previous().text.to_lowercase();
49740
49741        // Parse the string value
49742        let value = self.parse_string()?;
49743        if value.is_none() {
49744            return Ok(None);
49745        }
49746
49747        // Expect closing brace
49748        self.expect(TokenType::RBrace)?;
49749
49750        // Return appropriate expression based on type
49751        let value = value
49752            .ok_or_else(|| self.parse_error("Expected string value in ODBC datetime literal"))?;
49753        match type_indicator.as_str() {
49754            "d" => Ok(Some(Expression::Date(Box::new(UnaryFunc::new(value))))),
49755            "t" => Ok(Some(Expression::Time(Box::new(UnaryFunc::new(value))))),
49756            "ts" => Ok(Some(Expression::Timestamp(Box::new(TimestampFunc {
49757                this: Some(Box::new(value)),
49758                zone: None,
49759                with_tz: None,
49760                safe: None,
49761            })))),
49762            _ => Ok(Some(value)),
49763        }
49764    }
49765
49766    /// parse_offset - Parse OFFSET clause
49767    /// Python: if self._match(TokenType.OFFSET): return exp.Offset(this=self._parse_term())
49768    pub fn parse_offset(&mut self) -> Result<Option<Expression>> {
49769        if !self.match_token(TokenType::Offset) {
49770            return Ok(None);
49771        }
49772        // Parse the offset expression (usually a number)
49773        let offset_expr = self.parse_expression()?;
49774        Ok(Some(Expression::Offset(Box::new(Offset {
49775            this: offset_expr,
49776            rows: None,
49777        }))))
49778    }
49779
49780    /// parse_on_condition - Ported from Python _parse_on_condition
49781    #[allow(unused_variables, unused_mut)]
49782    /// parse_on_condition - Parses ON EMPTY/ERROR/NULL conditions
49783    /// Example: NULL ON EMPTY, ERROR ON ERROR
49784    pub fn parse_on_condition(&mut self) -> Result<Option<Expression>> {
49785        // Parse ON EMPTY
49786        let empty = if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
49787            Some(Box::new(Expression::Identifier(Identifier::new(
49788                "NULL".to_string(),
49789            ))))
49790        } else if self.match_text_seq(&["ERROR", "ON", "EMPTY"]) {
49791            Some(Box::new(Expression::Identifier(Identifier::new(
49792                "ERROR".to_string(),
49793            ))))
49794        } else if self.match_text_seq(&["DEFAULT"]) {
49795            let default_val = self.parse_expression()?;
49796            if self.match_text_seq(&["ON", "EMPTY"]) {
49797                Some(Box::new(default_val))
49798            } else {
49799                None
49800            }
49801        } else {
49802            None
49803        };
49804
49805        // Parse ON ERROR
49806        let error = if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
49807            Some(Box::new(Expression::Identifier(Identifier::new(
49808                "NULL".to_string(),
49809            ))))
49810        } else if self.match_text_seq(&["ERROR", "ON", "ERROR"]) {
49811            Some(Box::new(Expression::Identifier(Identifier::new(
49812                "ERROR".to_string(),
49813            ))))
49814        } else if self.match_text_seq(&["DEFAULT"]) {
49815            let default_val = self.parse_expression()?;
49816            if self.match_text_seq(&["ON", "ERROR"]) {
49817                Some(Box::new(default_val))
49818            } else {
49819                None
49820            }
49821        } else {
49822            None
49823        };
49824
49825        // Parse ON NULL
49826        let null = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
49827            Some(Box::new(Expression::Identifier(Identifier::new(
49828                "NULL".to_string(),
49829            ))))
49830        } else {
49831            None
49832        };
49833
49834        if empty.is_none() && error.is_none() && null.is_none() {
49835            return Ok(None);
49836        }
49837
49838        Ok(Some(Expression::OnCondition(Box::new(OnCondition {
49839            empty,
49840            error,
49841            null,
49842        }))))
49843    }
49844
49845    /// parse_on_handling - Implemented from Python _parse_on_handling
49846    /// Calls: parse_bitwise
49847    #[allow(unused_variables, unused_mut)]
49848    pub fn parse_on_handling(&mut self) -> Result<Option<Expression>> {
49849        if self.match_text_seq(&["ON"]) {
49850            // Matched: ON
49851            return Ok(None);
49852        }
49853        if self.match_text_seq(&["ON"]) {
49854            // Matched: ON
49855            return Ok(None);
49856        }
49857        Ok(None)
49858    }
49859
49860    /// parse_on_property - Implemented from Python _parse_on_property
49861    #[allow(unused_variables, unused_mut)]
49862    pub fn parse_on_property(&mut self) -> Result<Option<Expression>> {
49863        if self.match_text_seq(&["COMMIT", "PRESERVE", "ROWS"]) {
49864            return Ok(Some(Expression::OnCommitProperty(Box::new(
49865                OnCommitProperty { delete: None },
49866            ))));
49867        }
49868        if self.match_text_seq(&["COMMIT", "DELETE", "ROWS"]) {
49869            // Matched: COMMIT DELETE ROWS
49870            return Ok(None);
49871        }
49872        Ok(None)
49873    }
49874
49875    /// parse_opclass - Ported from Python _parse_opclass
49876    #[allow(unused_variables, unused_mut)]
49877    /// parse_opclass - Parses PostgreSQL operator class in index expressions
49878    /// Example: column_name text_pattern_ops
49879    pub fn parse_opclass(&mut self) -> Result<Option<Expression>> {
49880        // Parse the expression first
49881        let this = self.parse_expression()?;
49882
49883        // Check for keywords that would indicate this is not an opclass
49884        // (e.g., ASC, DESC, NULLS, etc.)
49885        if self.check(TokenType::Asc)
49886            || self.check(TokenType::Desc)
49887            || self.check(TokenType::Nulls)
49888            || self.check(TokenType::Comma)
49889            || self.check(TokenType::RParen)
49890        {
49891            return Ok(Some(this));
49892        }
49893
49894        // Try to parse an operator class name (table parts)
49895        if let Some(opclass_name) = self.parse_table()? {
49896            return Ok(Some(Expression::Opclass(Box::new(Opclass {
49897                this: Box::new(this),
49898                expression: Box::new(opclass_name),
49899            }))));
49900        }
49901
49902        Ok(Some(this))
49903    }
49904
49905    /// parse_open_json - Parses SQL Server OPENJSON function
49906    /// Example: OPENJSON(json, '$.path') WITH (col1 type '$.path' AS JSON, ...)
49907    pub fn parse_open_json(&mut self) -> Result<Option<Expression>> {
49908        // Parse the JSON expression
49909        let this = self.parse_expression()?;
49910
49911        // Parse optional path
49912        let path = if self.match_token(TokenType::Comma) {
49913            self.parse_string()?.map(Box::new)
49914        } else {
49915            None
49916        };
49917
49918        // Check for closing paren and WITH clause
49919        let expressions = if self.match_token(TokenType::RParen)
49920            && self.match_token(TokenType::With)
49921        {
49922            self.expect(TokenType::LParen)?;
49923            let mut cols = Vec::new();
49924            loop {
49925                // Parse column definition: name type 'path' [AS JSON]
49926                let col_name = self.parse_field()?;
49927                if col_name.is_none() {
49928                    break;
49929                }
49930                let col_type = self.parse_data_type()?;
49931                let col_path = self.parse_string()?.map(Box::new);
49932                let as_json = if self.match_token(TokenType::As) && self.match_identifier("JSON") {
49933                    Some(Box::new(Expression::Boolean(BooleanLiteral {
49934                        value: true,
49935                    })))
49936                } else {
49937                    None
49938                };
49939                cols.push(Expression::OpenJSONColumnDef(Box::new(OpenJSONColumnDef {
49940                    this: Box::new(col_name.ok_or_else(|| {
49941                        self.parse_error("Expected column name in OPENJSON WITH clause")
49942                    })?),
49943                    kind: String::new(), // kept for backwards compat, use data_type instead
49944                    path: col_path,
49945                    as_json,
49946                    data_type: Some(col_type),
49947                })));
49948                if !self.match_token(TokenType::Comma) {
49949                    break;
49950                }
49951            }
49952            self.expect(TokenType::RParen)?;
49953            cols
49954        } else {
49955            Vec::new()
49956        };
49957
49958        Ok(Some(Expression::OpenJSON(Box::new(OpenJSON {
49959            this: Box::new(this),
49960            path,
49961            expressions,
49962        }))))
49963    }
49964
49965    /// parse_operator - Ported from Python _parse_operator
49966    #[allow(unused_variables, unused_mut)]
49967    /// parse_operator - Parses PostgreSQL OPERATOR(op) syntax
49968    /// Example: col1 OPERATOR(~>) col2
49969    pub fn parse_operator(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
49970        let mut result = this;
49971
49972        // Parse OPERATOR(op) expressions
49973        while self.match_token(TokenType::LParen) {
49974            // Collect the operator text between parens
49975            let mut op_text = String::new();
49976            while !self.check(TokenType::RParen) && !self.is_at_end() {
49977                op_text.push_str(&self.peek().text);
49978                self.skip();
49979            }
49980            self.expect(TokenType::RParen)?;
49981
49982            // Parse the right-hand side expression
49983            let rhs = self.parse_expression()?;
49984
49985            result = Some(Expression::Operator(Box::new(Operator {
49986                this: Box::new(result.unwrap_or_else(|| Expression::Null(Null))),
49987                operator: Some(Box::new(Expression::Identifier(Identifier::new(op_text)))),
49988                expression: Box::new(rhs),
49989                comments: Vec::new(),
49990            })));
49991
49992            // Check if there's another OPERATOR keyword
49993            if !self.match_token(TokenType::Operator) {
49994                break;
49995            }
49996        }
49997
49998        Ok(result)
49999    }
50000
50001    /// parse_order - Parse ORDER BY clause
50002    /// Python: if not self._match(TokenType.ORDER_BY): return this; return exp.Order(expressions=self._parse_csv(self._parse_ordered))
50003    pub fn parse_order(&mut self) -> Result<Option<Expression>> {
50004        if !self.match_token(TokenType::Order) {
50005            return Ok(None);
50006        }
50007        // Consume BY if present
50008        self.match_token(TokenType::By);
50009
50010        // Parse comma-separated ordered expressions
50011        let mut expressions = Vec::new();
50012        loop {
50013            if let Some(ordered) = self.parse_ordered_item()? {
50014                expressions.push(ordered);
50015            } else {
50016                break;
50017            }
50018            if !self.match_token(TokenType::Comma) {
50019                break;
50020            }
50021        }
50022
50023        Ok(Some(Expression::OrderBy(Box::new(OrderBy {
50024            expressions,
50025            siblings: false,
50026            comments: Vec::new(),
50027        }))))
50028    }
50029
50030    /// parse_ordered_item - Parse a single ORDER BY item (expr [ASC|DESC] [NULLS FIRST|LAST])
50031    fn parse_ordered_item(&mut self) -> Result<Option<Ordered>> {
50032        // Parse the expression to order by
50033        let expr = match self.parse_expression() {
50034            Ok(e) => e,
50035            Err(_) => return Ok(None),
50036        };
50037
50038        // Check for ASC/DESC
50039        let mut desc = false;
50040        let mut explicit_asc = false;
50041        if self.match_token(TokenType::Asc) {
50042            explicit_asc = true;
50043        } else if self.match_token(TokenType::Desc) {
50044            desc = true;
50045        }
50046
50047        // Check for NULLS FIRST/LAST
50048        let nulls_first = if self.match_text_seq(&["NULLS", "FIRST"]) {
50049            Some(true)
50050        } else if self.match_text_seq(&["NULLS", "LAST"]) {
50051            Some(false)
50052        } else {
50053            None
50054        };
50055
50056        // Parse optional WITH FILL clause (ClickHouse)
50057        let with_fill = if self.match_text_seq(&["WITH", "FILL"]) {
50058            let from_ = if self.match_token(TokenType::From) {
50059                Some(Box::new(self.parse_or()?))
50060            } else {
50061                None
50062            };
50063            let to = if self.match_text_seq(&["TO"]) {
50064                Some(Box::new(self.parse_or()?))
50065            } else {
50066                None
50067            };
50068            let step = if self.match_text_seq(&["STEP"]) {
50069                Some(Box::new(self.parse_or()?))
50070            } else {
50071                None
50072            };
50073            let staleness = if self.match_text_seq(&["STALENESS"]) {
50074                Some(Box::new(self.parse_or()?))
50075            } else {
50076                None
50077            };
50078            let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
50079                if self.match_token(TokenType::LParen) {
50080                    let exprs = self.parse_expression_list()?;
50081                    self.expect(TokenType::RParen)?;
50082                    if exprs.len() == 1 {
50083                        Some(Box::new(exprs.into_iter().next().unwrap()))
50084                    } else {
50085                        Some(Box::new(Expression::Tuple(Box::new(
50086                            crate::expressions::Tuple { expressions: exprs },
50087                        ))))
50088                    }
50089                } else {
50090                    None
50091                }
50092            } else {
50093                None
50094            };
50095            Some(Box::new(WithFill {
50096                from_,
50097                to,
50098                step,
50099                staleness,
50100                interpolate,
50101            }))
50102        } else {
50103            None
50104        };
50105
50106        Ok(Some(Ordered {
50107            this: expr,
50108            desc,
50109            nulls_first,
50110            explicit_asc,
50111            with_fill,
50112        }))
50113    }
50114
50115    /// parse_ordered - Implemented from Python _parse_ordered (wrapper for parse_ordered_item)
50116    #[allow(unused_variables, unused_mut)]
50117    pub fn parse_ordered(&mut self) -> Result<Option<Expression>> {
50118        if let Some(ordered) = self.parse_ordered_item()? {
50119            return Ok(Some(Expression::Ordered(Box::new(ordered))));
50120        }
50121        if self.match_text_seq(&["NULLS", "FIRST"]) {
50122            return Ok(Some(Expression::WithFill(Box::new(WithFill {
50123                from_: None,
50124                to: None,
50125                step: None,
50126                staleness: None,
50127                interpolate: None,
50128            }))));
50129        }
50130        if self.match_text_seq(&["NULLS", "LAST"]) {
50131            // Matched: NULLS LAST
50132            return Ok(None);
50133        }
50134        if self.match_text_seq(&["WITH", "FILL"]) {
50135            // Matched: WITH FILL
50136            return Ok(None);
50137        }
50138        Ok(None)
50139    }
50140
50141    /// parse_overlay - Ported from Python _parse_overlay
50142    /// Parses OVERLAY function: OVERLAY(string PLACING replacement FROM position [FOR length])
50143    #[allow(unused_variables, unused_mut)]
50144    pub fn parse_overlay(&mut self) -> Result<Option<Expression>> {
50145        // Parse the string to be modified
50146        let this = match self.parse_bitwise() {
50147            Ok(Some(expr)) => expr,
50148            Ok(None) => return Ok(None),
50149            Err(e) => return Err(e),
50150        };
50151
50152        // Parse PLACING replacement (or comma then replacement)
50153        let replacement = if self.match_text_seq(&["PLACING"]) || self.match_token(TokenType::Comma)
50154        {
50155            match self.parse_bitwise() {
50156                Ok(Some(expr)) => expr,
50157                Ok(None) => {
50158                    return Err(self.parse_error("Expected replacement expression in OVERLAY"))
50159                }
50160                Err(e) => return Err(e),
50161            }
50162        } else {
50163            return Err(self.parse_error("Expected PLACING in OVERLAY function"));
50164        };
50165
50166        // Parse FROM position (or comma then position)
50167        let from = if self.match_token(TokenType::From) || self.match_token(TokenType::Comma) {
50168            match self.parse_bitwise() {
50169                Ok(Some(expr)) => expr,
50170                Ok(None) => return Err(self.parse_error("Expected position expression in OVERLAY")),
50171                Err(e) => return Err(e),
50172            }
50173        } else {
50174            return Err(self.parse_error("Expected FROM in OVERLAY function"));
50175        };
50176
50177        // Parse optional FOR length (or comma then length)
50178        let length = if self.match_token(TokenType::For) || self.match_token(TokenType::Comma) {
50179            match self.parse_bitwise() {
50180                Ok(Some(expr)) => Some(expr),
50181                Ok(None) => None,
50182                Err(_) => None,
50183            }
50184        } else {
50185            None
50186        };
50187
50188        Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
50189            this,
50190            replacement,
50191            from,
50192            length,
50193        }))))
50194    }
50195
50196    /// parse_parameter - Parse named parameter (@name or :name)
50197    /// Python: this = self._parse_identifier() or self._parse_primary_or_var(); return exp.Parameter(this=this)
50198    pub fn parse_parameter(&mut self) -> Result<Option<Expression>> {
50199        // Check for parameter token types
50200        if self.match_token(TokenType::Parameter) {
50201            let text = self.previous().text.clone();
50202            return Ok(Some(Expression::Parameter(Box::new(Parameter {
50203                name: Some(text),
50204                index: None,
50205                style: ParameterStyle::Colon,
50206                quoted: false,
50207                string_quoted: false,
50208                expression: None,
50209            }))));
50210        }
50211
50212        // Check for session parameter (@@name)
50213        if self.match_token(TokenType::SessionParameter) {
50214            let text = self.previous().text.clone();
50215            return Ok(Some(Expression::SessionParameter(Box::new(
50216                SessionParameter {
50217                    this: Box::new(Expression::Identifier(Identifier::new(text))),
50218                    kind: None,
50219                },
50220            ))));
50221        }
50222
50223        Ok(None)
50224    }
50225
50226    /// parse_paren - Ported from Python _parse_paren
50227    /// Parses parenthesized expressions: (expr), (select ...), or (a, b, c)
50228    #[allow(unused_variables, unused_mut)]
50229    pub fn parse_paren(&mut self) -> Result<Option<Expression>> {
50230        if !self.match_token(TokenType::LParen) {
50231            return Ok(None);
50232        }
50233
50234        // Check for empty tuple ()
50235        if self.match_token(TokenType::RParen) {
50236            return Ok(Some(Expression::Tuple(Box::new(Tuple {
50237                expressions: Vec::new(),
50238            }))));
50239        }
50240
50241        // Try to parse as subquery first
50242        // ClickHouse also allows (EXPLAIN ...) as subquery
50243        if self.check(TokenType::Select)
50244            || self.check(TokenType::With)
50245            || (matches!(
50246                self.config.dialect,
50247                Some(crate::dialects::DialectType::ClickHouse)
50248            ) && self.check(TokenType::Var)
50249                && self.peek().text.eq_ignore_ascii_case("EXPLAIN"))
50250        {
50251            let query = self.parse_statement()?;
50252            self.expect(TokenType::RParen)?;
50253            return Ok(Some(Expression::Subquery(Box::new(Subquery {
50254                this: query,
50255                alias: None,
50256                column_aliases: Vec::new(),
50257                order_by: None,
50258                limit: None,
50259                offset: None,
50260                lateral: false,
50261                modifiers_inside: true,
50262                trailing_comments: Vec::new(),
50263                distribute_by: None,
50264                sort_by: None,
50265                cluster_by: None,
50266                inferred_type: None,
50267            }))));
50268        }
50269
50270        // Parse comma-separated expressions
50271        let mut expressions = Vec::new();
50272        let mut trailing_comma = false;
50273        loop {
50274            match self.parse_expression() {
50275                Ok(expr) => expressions.push(expr),
50276                Err(_) => break,
50277            }
50278            if !self.match_token(TokenType::Comma) {
50279                break;
50280            }
50281            // ClickHouse: trailing comma makes a single-element tuple, e.g., (1,)
50282            if self.check(TokenType::RParen) {
50283                trailing_comma = true;
50284                break;
50285            }
50286        }
50287
50288        self.expect(TokenType::RParen)?;
50289
50290        // Single expression with trailing comma → tuple, e.g., (1,)
50291        if trailing_comma && expressions.len() == 1 {
50292            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
50293        }
50294
50295        // Single expression - return the unwrapped Paren
50296        if expressions.len() == 1 {
50297            return Ok(Some(Expression::Paren(Box::new(Paren {
50298                this: expressions.remove(0),
50299                trailing_comments: Vec::new(),
50300            }))));
50301        }
50302
50303        // Multiple expressions - return as tuple
50304        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
50305    }
50306
50307    /// parse_partition - Parses PARTITION/SUBPARTITION clause
50308    /// Python: _parse_partition
50309    pub fn parse_partition(&mut self) -> Result<Option<Expression>> {
50310        // PARTITION_KEYWORDS = {"PARTITION", "SUBPARTITION"}
50311        if !self.match_texts(&["PARTITION", "SUBPARTITION"]) {
50312            return Ok(None);
50313        }
50314
50315        let subpartition = self.previous().text.eq_ignore_ascii_case("SUBPARTITION");
50316
50317        // Parse wrapped CSV of disjunction expressions
50318        if !self.match_token(TokenType::LParen) {
50319            // Without parentheses, still return a Partition with empty expressions
50320            return Ok(Some(Expression::Partition(Box::new(Partition {
50321                expressions: Vec::new(),
50322                subpartition,
50323            }))));
50324        }
50325
50326        let mut expressions = Vec::new();
50327        loop {
50328            if let Some(expr) = self.parse_disjunction()? {
50329                expressions.push(expr);
50330            } else {
50331                break;
50332            }
50333
50334            if !self.match_token(TokenType::Comma) {
50335                break;
50336            }
50337        }
50338
50339        self.match_token(TokenType::RParen);
50340
50341        Ok(Some(Expression::Partition(Box::new(Partition {
50342            expressions,
50343            subpartition,
50344        }))))
50345    }
50346
50347    /// parse_partition_and_order - Delegates to parse_partition_by
50348    #[allow(unused_variables, unused_mut)]
50349    pub fn parse_partition_and_order(&mut self) -> Result<Option<Expression>> {
50350        self.parse_partition_by()
50351    }
50352
50353    /// parse_partition_bound_spec - Implemented from Python _parse_partition_bound_spec
50354    /// Calls: parse_bitwise, parse_number
50355    #[allow(unused_variables, unused_mut)]
50356    pub fn parse_partition_bound_spec_legacy(&mut self) -> Result<Option<Expression>> {
50357        if self.match_text_seq(&["MINVALUE"]) {
50358            return Ok(Some(Expression::PartitionBoundSpec(Box::new(
50359                PartitionBoundSpec {
50360                    this: None,
50361                    expression: None,
50362                    from_expressions: None,
50363                    to_expressions: None,
50364                },
50365            ))));
50366        }
50367        if self.match_text_seq(&["MAXVALUE"]) {
50368            // Matched: MAXVALUE
50369            return Ok(None);
50370        }
50371        if self.match_text_seq(&["TO"]) {
50372            // Matched: TO
50373            return Ok(None);
50374        }
50375        Ok(None)
50376    }
50377
50378    /// parse_partition_by - Ported from Python _parse_partition_by
50379    /// Parses PARTITION BY expression list
50380    #[allow(unused_variables, unused_mut)]
50381    pub fn parse_partition_by(&mut self) -> Result<Option<Expression>> {
50382        if !self.match_keywords(&[TokenType::Partition, TokenType::By]) {
50383            return Ok(None);
50384        }
50385        let expressions = self.parse_expression_list()?;
50386        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
50387    }
50388
50389    /// parse_partitioned_by - Parses PARTITIONED BY clause
50390    /// Python: _parse_partitioned_by
50391    pub fn parse_partitioned_by(&mut self) -> Result<Option<Expression>> {
50392        // Optionally match '='
50393        self.match_token(TokenType::Eq);
50394
50395        // Try to parse a schema first
50396        if let Some(schema) = self.parse_schema()? {
50397            return Ok(Some(Expression::PartitionedByProperty(Box::new(
50398                PartitionedByProperty {
50399                    this: Box::new(schema),
50400                },
50401            ))));
50402        }
50403
50404        // Fall back to bracket(field)
50405        if let Some(bracket) = self.parse_bracket()? {
50406            return Ok(Some(Expression::PartitionedByProperty(Box::new(
50407                PartitionedByProperty {
50408                    this: Box::new(bracket),
50409                },
50410            ))));
50411        }
50412
50413        // Try to parse a field directly
50414        if let Some(field) = self.parse_field()? {
50415            return Ok(Some(Expression::PartitionedByProperty(Box::new(
50416                PartitionedByProperty {
50417                    this: Box::new(field),
50418                },
50419            ))));
50420        }
50421
50422        Ok(None)
50423    }
50424
50425    /// parse_partitioned_by_bucket_or_truncate - Parses BUCKET or TRUNCATE partition transforms
50426    /// Python: _parse_partitioned_by_bucket_or_truncate
50427    /// Syntax: BUCKET(col, num_buckets) or TRUNCATE(col, width)
50428    /// Handles both Hive (num, col) and Trino (col, num) ordering, normalizes to (col, num)
50429    pub fn parse_partitioned_by_bucket_or_truncate(&mut self) -> Result<Option<Expression>> {
50430        // If no L_PAREN follows, this should be parsed as an identifier, not a function call
50431        if !self.check(TokenType::LParen) {
50432            // Retreat: go back one token (previous was BUCKET or TRUNCATE)
50433            if self.current > 0 {
50434                self.current -= 1;
50435            }
50436            return Ok(None);
50437        }
50438
50439        // Determine if it's BUCKET or TRUNCATE based on previous token
50440        let is_bucket = self.previous().text.eq_ignore_ascii_case("BUCKET");
50441
50442        // Parse wrapped arguments
50443        self.expect(TokenType::LParen)?;
50444        let mut args = Vec::new();
50445
50446        if !self.check(TokenType::RParen) {
50447            loop {
50448                // Try to parse primary or column
50449                if let Some(expr) = self.parse_primary_or_var()? {
50450                    args.push(expr);
50451                } else if let Some(col) = self.parse_column()? {
50452                    args.push(col);
50453                }
50454
50455                if !self.match_token(TokenType::Comma) {
50456                    break;
50457                }
50458            }
50459        }
50460        self.match_token(TokenType::RParen);
50461
50462        // Get first two arguments
50463        let (mut this, mut expr) = (args.get(0).cloned(), args.get(1).cloned());
50464
50465        // Normalize: if first arg is a Literal, swap (Hive uses (num, col), Trino uses (col, num))
50466        // We canonicalize to (col, num)
50467        if let Some(Expression::Literal(_)) = &this {
50468            std::mem::swap(&mut this, &mut expr);
50469        }
50470
50471        // Ensure we have both arguments
50472        let this_expr = this.unwrap_or(Expression::Null(Null));
50473        let expr_expr = expr.unwrap_or(Expression::Null(Null));
50474
50475        if is_bucket {
50476            Ok(Some(Expression::PartitionedByBucket(Box::new(
50477                PartitionedByBucket {
50478                    this: Box::new(this_expr),
50479                    expression: Box::new(expr_expr),
50480                },
50481            ))))
50482        } else {
50483            Ok(Some(Expression::PartitionByTruncate(Box::new(
50484                PartitionByTruncate {
50485                    this: Box::new(this_expr),
50486                    expression: Box::new(expr_expr),
50487                },
50488            ))))
50489        }
50490    }
50491
50492    /// parse_doris_partition_by_range_or_list - Parses Doris PARTITION BY RANGE/LIST syntax
50493    /// Handles:
50494    ///   PARTITION BY RANGE (`col`) (PARTITION name VALUES LESS THAN (val), ...)
50495    ///   PARTITION BY RANGE (`col`) (PARTITION name VALUES [(val1), (val2)), ...)
50496    ///   PARTITION BY RANGE (`col`) (FROM ('start') TO ('end') INTERVAL n UNIT)
50497    ///   PARTITION BY LIST (`col`) (PARTITION name VALUES IN (val1, val2), ...)
50498    fn parse_doris_partition_by_range_or_list(&mut self, kind: &str) -> Result<Expression> {
50499        // Parse partition column expressions: (`col1`, `col2`, ...) or (STR2DATE(col, fmt))
50500        // Use parse_wrapped_csv to handle function calls in partition columns
50501        let partition_expressions = self.parse_wrapped_csv()?;
50502
50503        // Check for partition definitions in parentheses
50504        let create_expressions = if self.check(TokenType::LParen) {
50505            self.skip(); // consume (
50506
50507            if kind == "LIST" {
50508                // Parse LIST partition definitions: PARTITION name VALUES IN (val1, val2), ...
50509                let partitions = self.parse_doris_list_partition_definitions()?;
50510                self.expect(TokenType::RParen)?;
50511                Some(Box::new(Expression::Tuple(Box::new(Tuple {
50512                    expressions: partitions,
50513                }))))
50514            } else {
50515                // RANGE: check for FROM (dynamic), START (StarRocks dynamic), or PARTITION (static)
50516                if self.check(TokenType::From) {
50517                    // Dynamic: FROM ('start') TO ('end') INTERVAL n UNIT
50518                    let dynamic_expr = self.parse_doris_dynamic_partition()?;
50519                    self.expect(TokenType::RParen)?;
50520                    Some(Box::new(dynamic_expr))
50521                } else if self.check(TokenType::Start) {
50522                    // StarRocks dynamic: START ('val') END ('val') EVERY (expr), ...
50523                    let mut dynamics = Vec::new();
50524                    loop {
50525                        if !self.check(TokenType::Start) {
50526                            break;
50527                        }
50528                        let dynamic_expr = self.parse_starrocks_start_end_every()?;
50529                        dynamics.push(dynamic_expr);
50530                        if !self.match_token(TokenType::Comma) {
50531                            break;
50532                        }
50533                    }
50534                    self.expect(TokenType::RParen)?;
50535                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
50536                        expressions: dynamics,
50537                    }))))
50538                } else if self.check(TokenType::Partition) {
50539                    // Static: PARTITION name VALUES LESS THAN (val) or VALUES [(val1), (val2))
50540                    let partitions = self.parse_doris_range_partition_definitions()?;
50541                    self.expect(TokenType::RParen)?;
50542                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
50543                        expressions: partitions,
50544                    }))))
50545                } else {
50546                    self.expect(TokenType::RParen)?;
50547                    None
50548                }
50549            }
50550        } else {
50551            None
50552        };
50553
50554        if kind == "LIST" {
50555            Ok(Expression::PartitionByListProperty(Box::new(
50556                PartitionByListProperty {
50557                    partition_expressions: partition_expressions.map(Box::new),
50558                    create_expressions,
50559                },
50560            )))
50561        } else {
50562            Ok(Expression::PartitionByRangeProperty(Box::new(
50563                PartitionByRangeProperty {
50564                    partition_expressions: partition_expressions.map(Box::new),
50565                    create_expressions,
50566                },
50567            )))
50568        }
50569    }
50570
50571    /// Parse Doris LIST partition definitions: PARTITION name VALUES IN (val1, val2), ...
50572    fn parse_doris_list_partition_definitions(&mut self) -> Result<Vec<Expression>> {
50573        let mut partitions = Vec::new();
50574        loop {
50575            if !self.match_token(TokenType::Partition) {
50576                break;
50577            }
50578            let name = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
50579            self.match_text_seq(&["VALUES", "IN"]);
50580            let values = self.parse_wrapped_csv_expressions()?;
50581
50582            let part_list = Expression::PartitionList(Box::new(PartitionList {
50583                this: Box::new(name),
50584                expressions: values,
50585            }));
50586            partitions.push(Expression::Partition(Box::new(Partition {
50587                expressions: vec![part_list],
50588                subpartition: false,
50589            })));
50590
50591            if !self.match_token(TokenType::Comma) {
50592                break;
50593            }
50594        }
50595        Ok(partitions)
50596    }
50597
50598    /// Parse Doris RANGE partition definitions
50599    fn parse_doris_range_partition_definitions(&mut self) -> Result<Vec<Expression>> {
50600        let mut partitions = Vec::new();
50601        loop {
50602            if !self.match_token(TokenType::Partition) {
50603                break;
50604            }
50605            let name = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
50606            self.match_text_seq(&["VALUES"]);
50607
50608            let part_range = if self.match_text_seq(&["LESS", "THAN"]) {
50609                if self.match_token(TokenType::Maxvalue) {
50610                    // VALUES LESS THAN MAXVALUE (without parens)
50611                    Expression::PartitionRange(Box::new(PartitionRange {
50612                        this: Box::new(name),
50613                        expression: None,
50614                        expressions: vec![Expression::Identifier(Identifier::new("MAXVALUE"))],
50615                    }))
50616                } else {
50617                    // VALUES LESS THAN (val) or VALUES LESS THAN (MAXVALUE)
50618                    let values = self.parse_wrapped_csv_expressions()?;
50619                    Expression::PartitionRange(Box::new(PartitionRange {
50620                        this: Box::new(name),
50621                        expression: None,
50622                        expressions: values,
50623                    }))
50624                }
50625            } else if self.check(TokenType::LBracket) {
50626                // VALUES [(val1), (val2)) - note asymmetric brackets
50627                self.skip(); // consume [
50628                let mut value_tuples = Vec::new();
50629                loop {
50630                    let vals = self.parse_wrapped_csv_expressions()?;
50631                    // Wrap in a Tuple for each (val)
50632                    value_tuples.push(Expression::Tuple(Box::new(Tuple { expressions: vals })));
50633                    if !self.match_token(TokenType::Comma) {
50634                        break;
50635                    }
50636                }
50637                // Expect ) to close the asymmetric bracket
50638                self.expect(TokenType::RParen)?;
50639                Expression::PartitionRange(Box::new(PartitionRange {
50640                    this: Box::new(name),
50641                    expression: None,
50642                    expressions: value_tuples,
50643                }))
50644            } else {
50645                // Fallback: no values
50646                Expression::PartitionRange(Box::new(PartitionRange {
50647                    this: Box::new(name),
50648                    expression: None,
50649                    expressions: Vec::new(),
50650                }))
50651            };
50652
50653            partitions.push(Expression::Partition(Box::new(Partition {
50654                expressions: vec![part_range],
50655                subpartition: false,
50656            })));
50657
50658            if !self.match_token(TokenType::Comma) {
50659                break;
50660            }
50661        }
50662        Ok(partitions)
50663    }
50664
50665    /// Parse Doris dynamic partition: FROM ('start') TO ('end') INTERVAL n UNIT
50666    fn parse_doris_dynamic_partition(&mut self) -> Result<Expression> {
50667        self.expect(TokenType::From)?;
50668        let start = self.parse_wrapped_expression()?;
50669        self.expect(TokenType::To)?;
50670        let end = self.parse_wrapped_expression()?;
50671
50672        // Parse INTERVAL n UNIT
50673        let every = if self.match_token(TokenType::Interval) {
50674            let number = self.parse_expression()?;
50675            let unit = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
50676                let unit_text = self.advance().text.to_ascii_uppercase();
50677                // Convert unit text to IntervalUnit
50678                let interval_unit = match unit_text.as_str() {
50679                    "YEAR" | "YEARS" => crate::expressions::IntervalUnit::Year,
50680                    "MONTH" | "MONTHS" => crate::expressions::IntervalUnit::Month,
50681                    "DAY" | "DAYS" => crate::expressions::IntervalUnit::Day,
50682                    "HOUR" | "HOURS" => crate::expressions::IntervalUnit::Hour,
50683                    "MINUTE" | "MINUTES" => crate::expressions::IntervalUnit::Minute,
50684                    "SECOND" | "SECONDS" => crate::expressions::IntervalUnit::Second,
50685                    _ => crate::expressions::IntervalUnit::Day, // Default fallback
50686                };
50687                Some(crate::expressions::IntervalUnitSpec::Simple {
50688                    unit: interval_unit,
50689                    use_plural: unit_text.ends_with('S'),
50690                })
50691            } else {
50692                None
50693            };
50694            Some(Box::new(Expression::Interval(Box::new(Interval {
50695                this: Some(number),
50696                unit,
50697            }))))
50698        } else {
50699            None
50700        };
50701
50702        Ok(Expression::PartitionByRangePropertyDynamic(Box::new(
50703            PartitionByRangePropertyDynamic {
50704                this: None,
50705                start: Some(Box::new(start)),
50706                end: Some(Box::new(end)),
50707                every,
50708                use_start_end: false,
50709            },
50710        )))
50711    }
50712
50713    /// Parse StarRocks START ('val') END ('val') EVERY (expr) syntax
50714    fn parse_starrocks_start_end_every(&mut self) -> Result<Expression> {
50715        self.expect(TokenType::Start)?;
50716        let start = self.parse_wrapped_expression()?;
50717        self.expect(TokenType::End)?;
50718        let end = self.parse_wrapped_expression()?;
50719
50720        // Parse EVERY (expr)
50721        let every = if self.match_identifier("EVERY") {
50722            self.expect(TokenType::LParen)?;
50723            let expr = self.parse_expression()?;
50724            self.expect(TokenType::RParen)?;
50725            Some(Box::new(expr))
50726        } else {
50727            None
50728        };
50729
50730        Ok(Expression::PartitionByRangePropertyDynamic(Box::new(
50731            PartitionByRangePropertyDynamic {
50732                this: None,
50733                start: Some(Box::new(start)),
50734                end: Some(Box::new(end)),
50735                every,
50736                use_start_end: true,
50737            },
50738        )))
50739    }
50740
50741    /// Parse wrapped comma-separated expressions: (expr, expr, ...)
50742    fn parse_wrapped_csv_expressions(&mut self) -> Result<Vec<Expression>> {
50743        self.expect(TokenType::LParen)?;
50744        let mut exprs = Vec::new();
50745        if !self.check(TokenType::RParen) {
50746            loop {
50747                // Check for MAXVALUE special keyword
50748                if self.match_token(TokenType::Maxvalue) {
50749                    exprs.push(Expression::Var(Box::new(Var {
50750                        this: "MAXVALUE".to_string(),
50751                    })));
50752                } else {
50753                    exprs.push(self.parse_expression()?);
50754                }
50755                if !self.match_token(TokenType::Comma) {
50756                    break;
50757                }
50758            }
50759        }
50760        self.expect(TokenType::RParen)?;
50761        Ok(exprs)
50762    }
50763
50764    /// Parse a single wrapped expression: (expr)
50765    fn parse_wrapped_expression(&mut self) -> Result<Expression> {
50766        self.expect(TokenType::LParen)?;
50767        let expr = self.parse_expression()?;
50768        self.expect(TokenType::RParen)?;
50769        Ok(expr)
50770    }
50771
50772    /// parse_partitioned_of - Implemented from Python _parse_partitioned_of
50773    #[allow(unused_variables, unused_mut)]
50774    pub fn parse_partitioned_of(&mut self) -> Result<Option<Expression>> {
50775        if self.match_text_seq(&["OF"]) {
50776            return Ok(Some(Expression::PartitionBoundSpec(Box::new(
50777                PartitionBoundSpec {
50778                    this: None,
50779                    expression: None,
50780                    from_expressions: None,
50781                    to_expressions: None,
50782                },
50783            ))));
50784        }
50785        if self.match_text_seq(&["FOR", "VALUES"]) {
50786            // Matched: FOR VALUES
50787            return Ok(None);
50788        }
50789        Ok(None)
50790    }
50791
50792    /// parse_period_for_system_time - Parses PERIOD FOR SYSTEM_TIME constraint
50793    /// Python: _parse_period_for_system_time
50794    /// Syntax: PERIOD FOR SYSTEM_TIME (start_col, end_col)
50795    pub fn parse_period_for_system_time(&mut self) -> Result<Option<Expression>> {
50796        // Check for SYSTEM_TIME / TIMESTAMP_SNAPSHOT token
50797        if !self.match_token(TokenType::TimestampSnapshot) {
50798            // Retreat: go back one token
50799            if self.current > 0 {
50800                self.current -= 1;
50801            }
50802            return Ok(None);
50803        }
50804
50805        // Parse wrapped id vars (two column names)
50806        let id_vars = self.parse_wrapped_id_vars()?;
50807
50808        // Extract the two columns from the tuple
50809        let (this, expression) = if let Some(Expression::Tuple(tuple)) = id_vars {
50810            let exprs = &tuple.expressions;
50811            (
50812                exprs.get(0).cloned().unwrap_or(Expression::Null(Null)),
50813                exprs.get(1).cloned().unwrap_or(Expression::Null(Null)),
50814            )
50815        } else {
50816            return Ok(None);
50817        };
50818
50819        Ok(Some(Expression::PeriodForSystemTimeConstraint(Box::new(
50820            PeriodForSystemTimeConstraint {
50821                this: Box::new(this),
50822                expression: Box::new(expression),
50823            },
50824        ))))
50825    }
50826
50827    /// parse_pipe_syntax_aggregate - Implemented from Python _parse_pipe_syntax_aggregate
50828    #[allow(unused_variables, unused_mut)]
50829    pub fn parse_pipe_syntax_aggregate(&mut self) -> Result<Option<Expression>> {
50830        if self.match_text_seq(&["AGGREGATE"]) {
50831            return Ok(Some(Expression::Select(Box::new(Select {
50832                expressions: Vec::new(),
50833                from: None,
50834                joins: Vec::new(),
50835                lateral_views: Vec::new(),
50836                prewhere: None,
50837                where_clause: None,
50838                group_by: None,
50839                having: None,
50840                qualify: None,
50841                order_by: None,
50842                distribute_by: None,
50843                cluster_by: None,
50844                sort_by: None,
50845                limit: None,
50846                offset: None,
50847                limit_by: None,
50848                fetch: None,
50849                distinct: false,
50850                distinct_on: None,
50851                top: None,
50852                with: None,
50853                sample: None,
50854                settings: None,
50855                format: None,
50856                windows: None,
50857                hint: None,
50858                connect: None,
50859                into: None,
50860                locks: Vec::new(),
50861                for_xml: Vec::new(),
50862                leading_comments: Vec::new(),
50863                post_select_comments: Vec::new(),
50864                kind: None,
50865                operation_modifiers: Vec::new(),
50866                qualify_after_window: false,
50867                option: None,
50868                exclude: None,
50869            }))));
50870        }
50871        if self.match_text_seq(&["GROUP", "AND"]) {
50872            // Matched: GROUP AND
50873            return Ok(None);
50874        }
50875        Ok(None)
50876    }
50877
50878    /// parse_pipe_syntax_aggregate_fields - Implemented from Python _parse_pipe_syntax_aggregate_fields
50879    /// Calls: parse_disjunction
50880    #[allow(unused_variables, unused_mut)]
50881    pub fn parse_pipe_syntax_aggregate_fields(&mut self) -> Result<Option<Expression>> {
50882        if self.match_text_seq(&["GROUP", "AND"]) {
50883            // Matched: GROUP AND
50884            return Ok(None);
50885        }
50886        Ok(None)
50887    }
50888
50889    /// parse_pipe_syntax_aggregate_group_order_by - Parses pipe syntax aggregate fields with grouping and ordering
50890    /// Python: _parse_pipe_syntax_aggregate_group_order_by
50891    /// Parses comma-separated aggregate fields and separates them into aggregates/groups and ORDER BY specs
50892    /// Returns a Tuple with two elements: (aggregates_and_groups, order_by_specs)
50893    pub fn parse_pipe_syntax_aggregate_group_order_by(&mut self) -> Result<Option<Expression>> {
50894        // Parse CSV of pipe syntax aggregate fields
50895        let mut aggregates_or_groups = Vec::new();
50896        let mut orders = Vec::new();
50897
50898        loop {
50899            if let Some(element) = self.parse_pipe_syntax_aggregate_fields()? {
50900                // Check if it's an Ordered expression (ORDER BY spec)
50901                match &element {
50902                    Expression::Ordered(ordered) => {
50903                        // Extract the inner expression, potentially adjusting for alias
50904                        let this = match &ordered.this {
50905                            Expression::Alias(alias) => {
50906                                // Use the alias name as an Identifier expression
50907                                Expression::Identifier(alias.alias.clone())
50908                            }
50909                            other => other.clone(),
50910                        };
50911                        // Add modified Ordered to orders
50912                        orders.push(Expression::Ordered(Box::new(Ordered {
50913                            this: this.clone(),
50914                            desc: ordered.desc,
50915                            nulls_first: ordered.nulls_first,
50916                            explicit_asc: ordered.explicit_asc,
50917                            with_fill: ordered.with_fill.clone(),
50918                        })));
50919                        aggregates_or_groups.push(this);
50920                    }
50921                    _ => {
50922                        aggregates_or_groups.push(element);
50923                    }
50924                }
50925            }
50926
50927            if !self.match_token(TokenType::Comma) {
50928                break;
50929            }
50930        }
50931
50932        if aggregates_or_groups.is_empty() && orders.is_empty() {
50933            return Ok(None);
50934        }
50935
50936        // Return a tuple with (aggregates_or_groups, orders)
50937        Ok(Some(Expression::Tuple(Box::new(Tuple {
50938            expressions: vec![
50939                Expression::Tuple(Box::new(Tuple {
50940                    expressions: aggregates_or_groups,
50941                })),
50942                Expression::Tuple(Box::new(Tuple {
50943                    expressions: orders,
50944                })),
50945            ],
50946        }))))
50947    }
50948
50949    /// parse_pipe_syntax_extend - Implemented from Python _parse_pipe_syntax_extend
50950    #[allow(unused_variables, unused_mut)]
50951    pub fn parse_pipe_syntax_extend(&mut self) -> Result<Option<Expression>> {
50952        if self.match_text_seq(&["EXTEND"]) {
50953            return Ok(Some(Expression::Select(Box::new(Select {
50954                expressions: Vec::new(),
50955                from: None,
50956                joins: Vec::new(),
50957                lateral_views: Vec::new(),
50958                prewhere: None,
50959                where_clause: None,
50960                group_by: None,
50961                having: None,
50962                qualify: None,
50963                order_by: None,
50964                distribute_by: None,
50965                cluster_by: None,
50966                sort_by: None,
50967                limit: None,
50968                offset: None,
50969                limit_by: None,
50970                fetch: None,
50971                distinct: false,
50972                distinct_on: None,
50973                top: None,
50974                with: None,
50975                sample: None,
50976                settings: None,
50977                format: None,
50978                windows: None,
50979                hint: None,
50980                connect: None,
50981                into: None,
50982                locks: Vec::new(),
50983                for_xml: Vec::new(),
50984                leading_comments: Vec::new(),
50985                post_select_comments: Vec::new(),
50986                kind: None,
50987                operation_modifiers: Vec::new(),
50988                qualify_after_window: false,
50989                option: None,
50990                exclude: None,
50991            }))));
50992        }
50993        Ok(None)
50994    }
50995
50996    /// parse_pipe_syntax_join - Parses JOIN in BigQuery pipe syntax
50997    /// Python: _parse_pipe_syntax_join
50998    /// Format: |> JOIN table ON condition
50999    pub fn parse_pipe_syntax_join(&mut self) -> Result<Option<Expression>> {
51000        // Parse the JOIN clause
51001        self.parse_join()
51002    }
51003
51004    /// parse_pipe_syntax_limit - Parses LIMIT/OFFSET in BigQuery pipe syntax
51005    /// Python: _parse_pipe_syntax_limit
51006    /// Format: |> LIMIT n [OFFSET m]
51007    pub fn parse_pipe_syntax_limit(&mut self) -> Result<Option<Expression>> {
51008        // Parse the LIMIT clause
51009        let limit = self.parse_limit()?;
51010
51011        // Parse optional OFFSET
51012        let offset = self.parse_offset()?;
51013
51014        // Combine into a tuple if both present
51015        match (limit, offset) {
51016            (Some(l), Some(o)) => Ok(Some(Expression::Tuple(Box::new(Tuple {
51017                expressions: vec![l, o],
51018            })))),
51019            (Some(l), None) => Ok(Some(l)),
51020            (None, Some(o)) => Ok(Some(o)),
51021            (None, None) => Ok(None),
51022        }
51023    }
51024
51025    /// parse_pipe_syntax_pivot - Parses PIVOT in BigQuery pipe syntax
51026    /// Python: _parse_pipe_syntax_pivot
51027    /// Format: |> PIVOT (agg_function FOR column IN (values))
51028    pub fn parse_pipe_syntax_pivot(&mut self) -> Result<Option<Expression>> {
51029        // For pipe syntax, we don't have a source yet - return pivot aggregation
51030        // The actual pivot parsing will be done in the query transformer
51031        self.parse_pivot_aggregation()
51032    }
51033
51034    /// parse_pipe_syntax_query - Parses a query with pipe syntax transformations
51035    /// Python: _parse_pipe_syntax_query
51036    /// Handles queries like: FROM table |> WHERE ... |> SELECT ... |> AGGREGATE ...
51037    pub fn parse_pipe_syntax_query(&mut self) -> Result<Option<Expression>> {
51038        // Start with a base query (could be a FROM clause or subquery)
51039        let mut query = self.parse_select_query()?;
51040
51041        if query.is_none() {
51042            return Ok(None);
51043        }
51044
51045        // Process pipe syntax chain: |> transform1 |> transform2 |> ...
51046        while self.match_token(TokenType::PipeGt) {
51047            let start_pos = self.current;
51048            let operator_text = self.peek().text.to_ascii_uppercase();
51049
51050            // Try to match known pipe syntax transforms
51051            let transform_result = match operator_text.as_str() {
51052                "WHERE" => {
51053                    self.skip();
51054                    self.parse_where()?
51055                }
51056                "SELECT" => {
51057                    self.skip();
51058                    self.parse_pipe_syntax_select()?
51059                }
51060                "AGGREGATE" => {
51061                    self.skip();
51062                    self.parse_pipe_syntax_aggregate()?
51063                }
51064                "EXTEND" => {
51065                    self.skip();
51066                    self.parse_pipe_syntax_extend()?
51067                }
51068                "LIMIT" => {
51069                    self.skip();
51070                    self.parse_pipe_syntax_limit()?
51071                }
51072                "JOIN" | "LEFT" | "RIGHT" | "INNER" | "OUTER" | "CROSS" | "FULL" => {
51073                    self.parse_pipe_syntax_join()?
51074                }
51075                "UNION" | "INTERSECT" | "EXCEPT" => self.parse_pipe_syntax_set_operator()?,
51076                "PIVOT" => {
51077                    self.skip();
51078                    self.parse_pipe_syntax_pivot()?
51079                }
51080                "TABLESAMPLE" => {
51081                    self.skip();
51082                    self.parse_pipe_syntax_tablesample()?
51083                }
51084                _ => {
51085                    // Try set operator or join as fallback
51086                    let set_op = self.parse_pipe_syntax_set_operator()?;
51087                    if set_op.is_some() {
51088                        set_op
51089                    } else {
51090                        let join_op = self.parse_pipe_syntax_join()?;
51091                        if join_op.is_some() {
51092                            join_op
51093                        } else {
51094                            // Unsupported operator, retreat and break
51095                            self.current = start_pos;
51096                            break;
51097                        }
51098                    }
51099                }
51100            };
51101
51102            // Apply transform to query
51103            if let Some(transform) = transform_result {
51104                // Wrap current query with transform in a PipeOperator
51105                let current_query = query.ok_or_else(|| {
51106                    self.parse_error("Expected base query before pipe syntax transform")
51107                })?;
51108                query = Some(Expression::PipeOperator(Box::new(PipeOperator {
51109                    this: current_query,
51110                    expression: transform,
51111                })));
51112            }
51113        }
51114
51115        Ok(query)
51116    }
51117
51118    /// parse_pipe_syntax_select - Parses SELECT in BigQuery pipe syntax
51119    /// Python: _parse_pipe_syntax_select
51120    /// Format: |> SELECT expressions
51121    pub fn parse_pipe_syntax_select(&mut self) -> Result<Option<Expression>> {
51122        // Parse the SELECT expressions without consuming the pipe
51123        let expressions = self.parse_expressions()?;
51124
51125        match expressions {
51126            Some(expr) => Ok(Some(expr)),
51127            None => Ok(Some(Expression::Star(Star {
51128                table: None,
51129                except: None,
51130                replace: None,
51131                rename: None,
51132                trailing_comments: Vec::new(),
51133                span: None,
51134            }))),
51135        }
51136    }
51137
51138    /// parse_pipe_syntax_set_operator - Parses set operation in BigQuery pipe syntax
51139    /// Python: _parse_pipe_syntax_set_operator
51140    /// Format: |> UNION ALL/INTERSECT/EXCEPT (subquery1, subquery2, ...)
51141    pub fn parse_pipe_syntax_set_operator(&mut self) -> Result<Option<Expression>> {
51142        // Try to parse as a set operation (UNION, INTERSECT, EXCEPT)
51143        if let Some(set_op) = self.parse_set_operations()? {
51144            Ok(Some(set_op))
51145        } else {
51146            Ok(None)
51147        }
51148    }
51149
51150    /// parse_pipe_syntax_tablesample - Parses TABLESAMPLE in BigQuery pipe syntax
51151    /// Python: _parse_pipe_syntax_tablesample
51152    /// Format: |> TABLESAMPLE SYSTEM (percent PERCENT)
51153    pub fn parse_pipe_syntax_tablesample(&mut self) -> Result<Option<Expression>> {
51154        // Parse the TABLESAMPLE clause
51155        self.parse_table_sample()
51156    }
51157
51158    /// parse_pivot_aggregation - Ported from Python _parse_pivot_aggregation
51159    /// Parses an aggregation function in PIVOT clause, optionally with alias
51160    #[allow(unused_variables, unused_mut)]
51161    pub fn parse_pivot_aggregation(&mut self) -> Result<Option<Expression>> {
51162        // Parse a function
51163        let func = self.parse_function()?;
51164
51165        if func.is_none() {
51166            // If previous token was a comma, silently return None
51167            if self.previous().token_type == TokenType::Comma {
51168                return Ok(None);
51169            }
51170            // Otherwise this could be an error, but we'll just return None
51171            return Ok(None);
51172        }
51173
51174        // Try to parse an alias for the function
51175        self.parse_alias_with_expr(func)
51176    }
51177
51178    /// parse_pivot_in - Parses the IN clause of a PIVOT
51179    /// Python: _parse_pivot_in
51180    /// Format: column IN (value1 [AS alias1], value2 [AS alias2], ...)
51181    pub fn parse_pivot_in(&mut self) -> Result<Option<Expression>> {
51182        // Parse the column being pivoted
51183        let value = self.parse_column()?;
51184        let value_expr = value.unwrap_or(Expression::Null(Null));
51185
51186        // Expect IN keyword
51187        if !self.match_token(TokenType::In) {
51188            return Err(self.parse_error("Expecting IN"));
51189        }
51190
51191        // Check if it's a parenthesized list or a field reference
51192        if self.match_token(TokenType::LParen) {
51193            // Check for ANY keyword
51194            let expressions = if self.match_text_seq(&["ANY"]) {
51195                // Parse PivotAny with optional ORDER BY
51196                let order = self.parse_order()?;
51197                vec![Expression::PivotAny(Box::new(PivotAny {
51198                    this: order.map(Box::new),
51199                }))]
51200            } else {
51201                // Parse comma-separated list of expressions, optionally aliased
51202                let mut exprs = Vec::new();
51203                loop {
51204                    if let Some(expr) = self.parse_select_or_expression()? {
51205                        // Check for alias
51206                        let final_expr = if self.match_token(TokenType::Alias) {
51207                            if let Some(alias) = self.parse_bitwise()? {
51208                                // Store the alias expression directly
51209                                Expression::PivotAlias(Box::new(PivotAlias { this: expr, alias }))
51210                            } else {
51211                                expr
51212                            }
51213                        } else {
51214                            expr
51215                        };
51216                        exprs.push(final_expr);
51217                    } else {
51218                        break;
51219                    }
51220                    if !self.match_token(TokenType::Comma) {
51221                        break;
51222                    }
51223                }
51224                exprs
51225            };
51226
51227            self.expect(TokenType::RParen)?;
51228
51229            Ok(Some(Expression::In(Box::new(In {
51230                this: value_expr,
51231                expressions,
51232                query: None,
51233                not: false,
51234                global: false,
51235                unnest: None,
51236                is_field: false,
51237            }))))
51238        } else {
51239            // Parse as a field reference: IN field_name
51240            let field = self.parse_id_var()?;
51241            // Convert field to expression and add to expressions
51242            let expressions = if let Some(f) = field {
51243                vec![f]
51244            } else {
51245                Vec::new()
51246            };
51247            Ok(Some(Expression::In(Box::new(In {
51248                this: value_expr,
51249                expressions,
51250                query: None,
51251                not: false,
51252                global: false,
51253                unnest: None,
51254                is_field: true,
51255            }))))
51256        }
51257    }
51258
51259    /// parse_pivots - Ported from Python _parse_pivots
51260    /// Parses one or more PIVOT/UNPIVOT clauses attached to a source expression
51261    /// Uses the existing parse_pivot/parse_unpivot methods
51262    pub fn parse_pivots_for_source(&mut self, source: Expression) -> Result<Option<Expression>> {
51263        let mut result = source;
51264
51265        loop {
51266            if self.match_token(TokenType::Pivot) {
51267                result = self.parse_pivot(result)?;
51268            } else if self.match_texts(&["UNPIVOT"]) {
51269                result = self.parse_unpivot(result)?;
51270            } else {
51271                break;
51272            }
51273        }
51274
51275        // Return None if no pivots were parsed
51276        if matches!(result, Expression::Null(_)) {
51277            Ok(None)
51278        } else {
51279            Ok(Some(result))
51280        }
51281    }
51282
51283    /// parse_placeholder - Parse placeholder token (? or :name)
51284    /// Python: if self._match_set(self.PLACEHOLDER_PARSERS): return placeholder
51285    pub fn parse_placeholder(&mut self) -> Result<Option<Expression>> {
51286        // Match positional placeholder (?)
51287        if self.match_token(TokenType::Placeholder) {
51288            return Ok(Some(Expression::Placeholder(Placeholder { index: None })));
51289        }
51290        // Match colon placeholder (:name) - handled by Parameter token
51291        if self.match_token(TokenType::Parameter) {
51292            let text = self.previous().text.clone();
51293            return Ok(Some(Expression::Parameter(Box::new(Parameter {
51294                name: Some(text),
51295                index: None,
51296                style: ParameterStyle::Colon,
51297                quoted: false,
51298                string_quoted: false,
51299                expression: None,
51300            }))));
51301        }
51302        Ok(None)
51303    }
51304
51305    /// Parse ClickHouse query parameter syntax: {name: Type}
51306    fn parse_clickhouse_braced_parameter(&mut self) -> Result<Option<Expression>> {
51307        if !matches!(
51308            self.config.dialect,
51309            Some(crate::dialects::DialectType::ClickHouse)
51310        ) {
51311            return Ok(None);
51312        }
51313        if !self.check(TokenType::LBrace) {
51314            return Ok(None);
51315        }
51316
51317        let start = self.current;
51318        self.skip(); // consume {
51319
51320        if !(self.is_identifier_token() || self.is_safe_keyword_as_identifier()) {
51321            self.current = start;
51322            return Ok(None);
51323        }
51324        let name = self.advance().text.clone();
51325
51326        if !self.match_token(TokenType::Colon) {
51327            self.current = start;
51328            return Ok(None);
51329        }
51330
51331        let kind_start = self.current;
51332        let mut paren_depth = 0usize;
51333        let mut bracket_depth = 0usize;
51334
51335        while !self.is_at_end() {
51336            let token_type = self.peek().token_type;
51337            match token_type {
51338                TokenType::LParen => {
51339                    paren_depth += 1;
51340                    self.skip();
51341                }
51342                TokenType::RParen => {
51343                    if paren_depth == 0 {
51344                        break;
51345                    }
51346                    paren_depth -= 1;
51347                    self.skip();
51348                }
51349                TokenType::LBracket => {
51350                    bracket_depth += 1;
51351                    self.skip();
51352                }
51353                TokenType::RBracket => {
51354                    if bracket_depth == 0 {
51355                        break;
51356                    }
51357                    bracket_depth -= 1;
51358                    self.skip();
51359                }
51360                TokenType::RBrace => {
51361                    if paren_depth == 0 && bracket_depth == 0 {
51362                        break;
51363                    }
51364                    self.skip();
51365                }
51366                _ => {
51367                    self.skip();
51368                }
51369            }
51370        }
51371
51372        if self.current <= kind_start || !self.match_token(TokenType::RBrace) {
51373            return Err(self.parse_error("Expected } in ClickHouse query parameter"));
51374        }
51375
51376        let kind = self
51377            .tokens_to_sql(kind_start, self.current - 1)
51378            .trim()
51379            .to_string();
51380        if kind.is_empty() {
51381            return Err(self.parse_error("Expected parameter kind in ClickHouse query parameter"));
51382        }
51383
51384        Ok(Some(Expression::Parameter(Box::new(Parameter {
51385            name: Some(name),
51386            index: None,
51387            style: ParameterStyle::Brace,
51388            quoted: false,
51389            string_quoted: false,
51390            expression: Some(kind),
51391        }))))
51392    }
51393
51394    /// parse_position - Ported from Python _parse_position
51395    /// Parses POSITION function: POSITION(substr IN str) or POSITION(needle, haystack, start)
51396    #[allow(unused_variables, unused_mut)]
51397    pub fn parse_position(&mut self) -> Result<Option<Expression>> {
51398        // Parse comma-separated arguments first
51399        let mut args: Vec<Expression> = Vec::new();
51400
51401        match self.parse_bitwise() {
51402            Ok(Some(expr)) => {
51403                let expr = self.maybe_clickhouse_alias(expr);
51404                let expr = self.try_clickhouse_func_arg_alias(expr);
51405                args.push(expr);
51406            }
51407            Ok(None) => return Ok(None),
51408            Err(e) => return Err(e),
51409        }
51410
51411        // Check for IN keyword (SQL standard syntax: POSITION(substr IN str))
51412        if self.match_token(TokenType::In) {
51413            match self.parse_bitwise() {
51414                Ok(Some(haystack)) => {
51415                    let haystack = self.maybe_clickhouse_alias(haystack);
51416                    let haystack = self.try_clickhouse_func_arg_alias(haystack);
51417                    return Ok(Some(Expression::StrPosition(Box::new(StrPosition {
51418                        this: Box::new(haystack),
51419                        substr: Some(Box::new(args.remove(0))),
51420                        position: None,
51421                        occurrence: None,
51422                    }))));
51423                }
51424                Ok(None) => {
51425                    return Err(self.parse_error("Expected expression after IN in POSITION"))
51426                }
51427                Err(e) => return Err(e),
51428            }
51429        }
51430
51431        // Parse comma-separated additional arguments
51432        while self.match_token(TokenType::Comma) {
51433            match self.parse_bitwise() {
51434                Ok(Some(expr)) => {
51435                    let expr = self.maybe_clickhouse_alias(expr);
51436                    let expr = self.try_clickhouse_func_arg_alias(expr);
51437                    args.push(expr);
51438                }
51439                Ok(None) => break,
51440                Err(e) => return Err(e),
51441            }
51442        }
51443
51444        // Function syntax: POSITION(needle, haystack, start?) or ClickHouse POSITION(haystack, needle, start?)
51445        let position = args.get(2).cloned();
51446        let (haystack, needle) = if matches!(
51447            self.config.dialect,
51448            Some(crate::dialects::DialectType::ClickHouse)
51449        ) {
51450            (args.get(0).cloned(), args.get(1).cloned())
51451        } else {
51452            (args.get(1).cloned(), args.get(0).cloned())
51453        };
51454
51455        Ok(Some(Expression::StrPosition(Box::new(StrPosition {
51456            this: Box::new(
51457                haystack.unwrap_or_else(|| {
51458                    Expression::Literal(Box::new(Literal::String("".to_string())))
51459                }),
51460            ),
51461            substr: needle.map(Box::new),
51462            position: position.map(Box::new),
51463            occurrence: None,
51464        }))))
51465    }
51466
51467    /// parse_prewhere - Ported from Python _parse_prewhere
51468    /// Parses PREWHERE clause (ClickHouse specific)
51469    #[allow(unused_variables, unused_mut)]
51470    pub fn parse_prewhere(&mut self) -> Result<Option<Expression>> {
51471        if !self.match_token(TokenType::Prewhere) {
51472            return Ok(None);
51473        }
51474        // Parse the condition expression
51475        let condition = self.parse_expression()?;
51476        Ok(Some(Expression::PreWhere(Box::new(PreWhere {
51477            this: condition,
51478        }))))
51479    }
51480
51481    /// parse_primary_key - Parses PRIMARY KEY constraint
51482    /// Python: _parse_primary_key
51483    /// Can return either PrimaryKeyColumnConstraint (column-level) or PrimaryKey (table-level)
51484    pub fn parse_primary_key(&mut self) -> Result<Option<Expression>> {
51485        self.parse_primary_key_impl(false, false)
51486    }
51487
51488    /// Implementation of parse_primary_key with options
51489    pub fn parse_primary_key_impl(
51490        &mut self,
51491        wrapped_optional: bool,
51492        in_props: bool,
51493    ) -> Result<Option<Expression>> {
51494        // Check for ASC/DESC
51495        let desc = if self.match_token(TokenType::Asc) {
51496            false
51497        } else if self.match_token(TokenType::Desc) {
51498            true
51499        } else {
51500            false
51501        };
51502
51503        // Parse optional constraint name (if current token is identifier and next is L_PAREN)
51504        let this = if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
51505            && self.check_next(TokenType::LParen)
51506        {
51507            self.parse_id_var()?
51508        } else {
51509            None
51510        };
51511
51512        // If not in_props and no L_PAREN ahead, return column-level constraint
51513        if !in_props && !self.check(TokenType::LParen) {
51514            let options = self.parse_key_constraint_options_list()?;
51515            return Ok(Some(Expression::PrimaryKeyColumnConstraint(Box::new(
51516                PrimaryKeyColumnConstraint {
51517                    desc: if desc {
51518                        Some(Box::new(Expression::Boolean(BooleanLiteral {
51519                            value: true,
51520                        })))
51521                    } else {
51522                        None
51523                    },
51524                    options,
51525                },
51526            ))));
51527        }
51528
51529        // Parse table-level PRIMARY KEY (column_list)
51530        let expressions = if self.match_token(TokenType::LParen) {
51531            let mut exprs = Vec::new();
51532            loop {
51533                if let Some(part) = self.parse_primary_key_part()? {
51534                    exprs.push(part);
51535                }
51536                if !self.match_token(TokenType::Comma) {
51537                    break;
51538                }
51539            }
51540            self.expect(TokenType::RParen)?;
51541            exprs
51542        } else if wrapped_optional {
51543            Vec::new()
51544        } else {
51545            return Err(self.parse_error("Expected '(' for PRIMARY KEY column list"));
51546        };
51547
51548        // Parse INCLUDE clause for covering index
51549        let include = self.parse_index_params()?;
51550
51551        // Parse constraint options
51552        let options = self.parse_key_constraint_options_list()?;
51553
51554        Ok(Some(Expression::PrimaryKey(Box::new(PrimaryKey {
51555            this: this.map(Box::new),
51556            expressions,
51557            options,
51558            include: include.map(Box::new),
51559        }))))
51560    }
51561
51562    /// Parse key constraint options as a list of expressions
51563    fn parse_key_constraint_options_list(&mut self) -> Result<Vec<Expression>> {
51564        let mut options = Vec::new();
51565
51566        loop {
51567            if self.is_at_end() {
51568                break;
51569            }
51570
51571            if self.match_token(TokenType::On) {
51572                // Parse ON DELETE/UPDATE action
51573                let on_what = if !self.is_at_end() {
51574                    let token = self.advance();
51575                    token.text.clone()
51576                } else {
51577                    break;
51578                };
51579
51580                let action = if self.match_text_seq(&["NO", "ACTION"]) {
51581                    "NO ACTION"
51582                } else if self.match_text_seq(&["CASCADE"]) {
51583                    "CASCADE"
51584                } else if self.match_text_seq(&["RESTRICT"]) {
51585                    "RESTRICT"
51586                } else if self.match_token(TokenType::Set) && self.match_token(TokenType::Null) {
51587                    "SET NULL"
51588                } else if self.match_token(TokenType::Set) && self.match_token(TokenType::Default) {
51589                    "SET DEFAULT"
51590                } else {
51591                    break;
51592                };
51593
51594                options.push(Expression::Var(Box::new(Var {
51595                    this: format!("ON {} {}", on_what, action),
51596                })));
51597            } else if self.match_text_seq(&["NOT", "ENFORCED"]) {
51598                options.push(Expression::Var(Box::new(Var {
51599                    this: "NOT ENFORCED".to_string(),
51600                })));
51601            } else if self.match_text_seq(&["DEFERRABLE"]) {
51602                options.push(Expression::Var(Box::new(Var {
51603                    this: "DEFERRABLE".to_string(),
51604                })));
51605            } else if self.match_text_seq(&["INITIALLY", "DEFERRED"]) {
51606                options.push(Expression::Var(Box::new(Var {
51607                    this: "INITIALLY DEFERRED".to_string(),
51608                })));
51609            } else if self.match_text_seq(&["NORELY"]) {
51610                options.push(Expression::Var(Box::new(Var {
51611                    this: "NORELY".to_string(),
51612                })));
51613            } else if self.match_text_seq(&["RELY"]) {
51614                options.push(Expression::Var(Box::new(Var {
51615                    this: "RELY".to_string(),
51616                })));
51617            } else {
51618                break;
51619            }
51620        }
51621
51622        Ok(options)
51623    }
51624
51625    /// parse_primary_key_part - Delegates to parse_field
51626    #[allow(unused_variables, unused_mut)]
51627    pub fn parse_primary_key_part(&mut self) -> Result<Option<Expression>> {
51628        // ClickHouse: PRIMARY KEY can contain full expressions (e.g., t.a, c0 IN (SELECT 1))
51629        if matches!(
51630            self.config.dialect,
51631            Some(crate::dialects::DialectType::ClickHouse)
51632        ) {
51633            return self.parse_expression().map(Some);
51634        }
51635        if (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
51636            && self.check_next(TokenType::LParen)
51637        {
51638            return self.parse_expression().map(Some);
51639        }
51640        if let Some(field) = self.parse_field()? {
51641            Ok(Some(field))
51642        } else {
51643            self.parse_expression().map(Some)
51644        }
51645    }
51646
51647    /// parse_primary_or_var - Parses a primary expression or variable
51648    /// Python: _parse_primary_or_var
51649    /// Returns: parse_primary() or parse_var(any_token=True)
51650    pub fn parse_primary_or_var(&mut self) -> Result<Option<Expression>> {
51651        // First try to parse a primary expression
51652        let saved_pos = self.current;
51653        match self.parse_primary() {
51654            Ok(expr) => return Ok(Some(expr)),
51655            Err(_) => {
51656                // Reset position and try parse_var
51657                self.current = saved_pos;
51658            }
51659        }
51660
51661        // Fall back to parsing a variable
51662        self.parse_var()
51663    }
51664
51665    /// parse_procedure_option - Implemented from Python _parse_procedure_option
51666    #[allow(unused_variables, unused_mut)]
51667    pub fn parse_procedure_option(&mut self) -> Result<Option<Expression>> {
51668        if self.match_text_seq(&["EXECUTE", "AS"]) {
51669            // Matched: EXECUTE AS
51670            return Ok(None);
51671        }
51672        Ok(None)
51673    }
51674
51675    /// parse_projections - Delegates to parse_expressions
51676    #[allow(unused_variables, unused_mut)]
51677    pub fn parse_projections(&mut self) -> Result<Option<Expression>> {
51678        self.parse_expressions()
51679    }
51680
51681    /// parse_properties - Parses table/column properties
51682    /// Python: _parse_properties
51683    /// Collects a list of properties using parse_property
51684    pub fn parse_properties(&mut self) -> Result<Option<Expression>> {
51685        self.parse_properties_impl(None)
51686    }
51687
51688    /// Implementation of parse_properties with before option
51689    pub fn parse_properties_impl(&mut self, before: Option<bool>) -> Result<Option<Expression>> {
51690        let mut properties = Vec::new();
51691
51692        loop {
51693            let prop = if before == Some(true) {
51694                self.parse_property_before()?
51695            } else {
51696                self.parse_property()?
51697            };
51698
51699            if let Some(p) = prop {
51700                properties.push(p);
51701            } else {
51702                break;
51703            }
51704        }
51705
51706        if properties.is_empty() {
51707            Ok(None)
51708        } else {
51709            Ok(Some(Expression::Properties(Box::new(Properties {
51710                expressions: properties,
51711            }))))
51712        }
51713    }
51714
51715    /// parse_property - Implemented from Python _parse_property
51716    /// Calls: parse_bitwise, parse_column, parse_sequence_properties
51717    #[allow(unused_variables, unused_mut)]
51718    pub fn parse_property(&mut self) -> Result<Option<Expression>> {
51719        if self.match_text_seq(&["COMPOUND", "SORTKEY"]) {
51720            return Ok(Some(Expression::Identifier(Identifier {
51721                name: String::new(),
51722                quoted: false,
51723                trailing_comments: Vec::new(),
51724                span: None,
51725            })));
51726        }
51727        if self.match_text_seq(&["SQL", "SECURITY"]) {
51728            // Matched: SQL SECURITY
51729            return Ok(None);
51730        }
51731        if self.match_texts(&["DEFINER", "INVOKER"]) {
51732            // Matched one of: DEFINER, INVOKER
51733            return Ok(None);
51734        }
51735        Ok(None)
51736    }
51737
51738    /// parse_on_cluster_clause - Parse ClickHouse ON CLUSTER clause
51739    fn parse_on_cluster_clause(&mut self) -> Result<Option<OnCluster>> {
51740        if !matches!(
51741            self.config.dialect,
51742            Some(crate::dialects::DialectType::ClickHouse)
51743        ) {
51744            return Ok(None);
51745        }
51746
51747        let start = self.current;
51748        if !self.match_token(TokenType::On) {
51749            return Ok(None);
51750        }
51751
51752        if !self.match_token(TokenType::Cluster) {
51753            self.current = start;
51754            return Ok(None);
51755        }
51756
51757        let this = if self.check(TokenType::String) {
51758            let value = self.expect_string()?;
51759            Expression::Literal(Box::new(Literal::String(value)))
51760        } else if let Some(id_expr) = self.parse_id_var()? {
51761            id_expr
51762        } else if self.is_safe_keyword_as_identifier() {
51763            let name = self.advance().text;
51764            Expression::Identifier(Identifier {
51765                name,
51766                quoted: false,
51767                trailing_comments: Vec::new(),
51768                span: None,
51769            })
51770        } else {
51771            return Err(self.parse_error("Expected cluster name after ON CLUSTER"));
51772        };
51773
51774        Ok(Some(OnCluster {
51775            this: Box::new(this),
51776        }))
51777    }
51778
51779    /// parse_clickhouse_table_properties - Parse ClickHouse table properties after column defs
51780    fn parse_clickhouse_table_properties(
51781        &mut self,
51782        properties: &mut Vec<Expression>,
51783    ) -> Result<()> {
51784        loop {
51785            if self.match_identifier("ENGINE") {
51786                self.match_token(TokenType::Eq);
51787                let engine = self.parse_clickhouse_engine_expression()?;
51788                properties.push(Expression::EngineProperty(Box::new(EngineProperty {
51789                    this: Box::new(engine),
51790                })));
51791                continue;
51792            }
51793
51794            if self.match_token(TokenType::Order) {
51795                self.expect(TokenType::By)?;
51796                let order_by = if matches!(
51797                    self.config.dialect,
51798                    Some(crate::dialects::DialectType::ClickHouse)
51799                ) && self.match_token(TokenType::LParen)
51800                {
51801                    // ClickHouse: ORDER BY (col1 [ASC|DESC], col2 [ASC|DESC], ...)
51802                    // or ORDER BY () for no ordering
51803                    if self.check(TokenType::RParen) {
51804                        self.skip();
51805                        OrderBy {
51806                            expressions: vec![Ordered::asc(Expression::Tuple(Box::new(Tuple {
51807                                expressions: Vec::new(),
51808                            })))],
51809                            siblings: false,
51810                            comments: Vec::new(),
51811                        }
51812                    } else {
51813                        // Parse all expressions inside the parentheses
51814                        let mut inner_exprs = Vec::new();
51815                        loop {
51816                            let expr = self.parse_expression()?;
51817                            inner_exprs.push(expr);
51818                            if !self.match_token(TokenType::Comma) {
51819                                break;
51820                            }
51821                        }
51822                        self.expect(TokenType::RParen)?;
51823                        // Wrap in a Tuple for multi-expr, Paren for single-expr
51824                        let wrapper = if inner_exprs.len() == 1 {
51825                            Expression::Paren(Box::new(Paren {
51826                                this: inner_exprs.into_iter().next().unwrap(),
51827                                trailing_comments: Vec::new(),
51828                            }))
51829                        } else {
51830                            Expression::Tuple(Box::new(Tuple {
51831                                expressions: inner_exprs,
51832                            }))
51833                        };
51834                        OrderBy {
51835                            expressions: vec![Ordered::asc(wrapper)],
51836                            siblings: false,
51837                            comments: Vec::new(),
51838                        }
51839                    }
51840                } else {
51841                    self.parse_order_by()?
51842                };
51843                properties.push(Expression::OrderBy(Box::new(order_by)));
51844                continue;
51845            }
51846
51847            if self.match_token(TokenType::Partition) {
51848                self.expect(TokenType::By)?;
51849                if self.check(TokenType::Order) && self.check_next(TokenType::By) {
51850                    return Err(self.parse_error("Expected expression after PARTITION BY"));
51851                }
51852                let expr = self
51853                    .parse_assignment()?
51854                    .ok_or_else(|| self.parse_error("Expected expression after PARTITION BY"))?;
51855                properties.push(Expression::PartitionedByProperty(Box::new(
51856                    PartitionedByProperty {
51857                        this: Box::new(expr),
51858                    },
51859                )));
51860                continue;
51861            }
51862
51863            if self.match_token(TokenType::PrimaryKey) {
51864                // ClickHouse supports PRIMARY KEY id and PRIMARY KEY (id, ...)
51865                let _ = self.match_token(TokenType::Key);
51866                if self.check(TokenType::LParen) {
51867                    if let Some(pk) = self.parse_primary_key_impl(false, true)? {
51868                        properties.push(pk);
51869                    }
51870                } else if let Some(expr) = self.parse_conjunction()? {
51871                    // ClickHouse: PRIMARY KEY expr (e.g., PRIMARY KEY tuple(), PRIMARY KEY id)
51872                    let mut exprs = vec![expr];
51873                    while self.match_token(TokenType::Comma) {
51874                        if let Some(next_expr) = self.parse_field()? {
51875                            exprs.push(next_expr);
51876                        } else {
51877                            break;
51878                        }
51879                    }
51880                    properties.push(Expression::PrimaryKey(Box::new(PrimaryKey {
51881                        this: None,
51882                        expressions: exprs,
51883                        options: Vec::new(),
51884                        include: None,
51885                    })));
51886                } else {
51887                    return Err(self.parse_error("Expected expression after PRIMARY KEY"));
51888                }
51889                continue;
51890            }
51891
51892            if self.match_token(TokenType::Sample) {
51893                let _ = self.match_token(TokenType::By);
51894                let expr = self.parse_expression()?;
51895                properties.push(Expression::SampleProperty(Box::new(SampleProperty {
51896                    this: Box::new(expr),
51897                })));
51898                continue;
51899            }
51900
51901            if self.match_token(TokenType::Settings) {
51902                let mut settings = Vec::new();
51903                loop {
51904                    settings.push(self.parse_expression()?);
51905                    if !self.match_token(TokenType::Comma) {
51906                        break;
51907                    }
51908                }
51909                properties.push(Expression::SettingsProperty(Box::new(SettingsProperty {
51910                    expressions: settings,
51911                })));
51912                continue;
51913            }
51914
51915            if self.match_token(TokenType::Comment) {
51916                let comment_expr = if self.check(TokenType::String) {
51917                    Expression::Literal(Box::new(Literal::String(self.expect_string()?)))
51918                } else {
51919                    self.parse_expression()?
51920                };
51921                properties.push(Expression::SchemaCommentProperty(Box::new(
51922                    SchemaCommentProperty {
51923                        this: Box::new(comment_expr),
51924                    },
51925                )));
51926                continue;
51927            }
51928
51929            // TTL time_column + INTERVAL '1' MONTH [DELETE|RECOMPRESS|TO DISK|TO VOLUME] [WHERE ...]
51930            if self.match_identifier("TTL") {
51931                if let Some(ttl_expr) = self.parse_ttl()? {
51932                    properties.push(ttl_expr);
51933                }
51934                continue;
51935            }
51936
51937            if self.match_identifier("SOURCE") {
51938                if let Some(prop) = self.parse_dict_property("SOURCE")? {
51939                    properties.push(prop);
51940                }
51941                continue;
51942            }
51943
51944            if self.match_identifier("LAYOUT") {
51945                if let Some(prop) = self.parse_dict_property("LAYOUT")? {
51946                    properties.push(prop);
51947                }
51948                continue;
51949            }
51950
51951            if self.match_identifier("LIFETIME") {
51952                if let Some(range) = self.parse_dict_range("LIFETIME")? {
51953                    properties.push(range);
51954                }
51955                continue;
51956            }
51957
51958            if self.match_identifier("RANGE") || self.match_token(TokenType::Range) {
51959                if let Some(range) = self.parse_dict_range("RANGE")? {
51960                    properties.push(range);
51961                }
51962                continue;
51963            }
51964
51965            break;
51966        }
51967
51968        Ok(())
51969    }
51970
51971    /// ClickHouse implicit alias in function arguments: `expr identifier` (without AS keyword).
51972    /// The token after the alias must be a delimiter (comma, RParen, FROM, FOR, AS).
51973    fn try_clickhouse_implicit_alias(&mut self, expr: Expression) -> Expression {
51974        if !matches!(
51975            self.config.dialect,
51976            Some(crate::dialects::DialectType::ClickHouse)
51977        ) {
51978            return expr;
51979        }
51980        if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
51981            let next_after = self.peek_nth(1).map(|t| t.token_type);
51982            let is_delimiter = matches!(
51983                next_after,
51984                Some(TokenType::Comma)
51985                    | Some(TokenType::RParen)
51986                    | Some(TokenType::From)
51987                    | Some(TokenType::For)
51988                    | Some(TokenType::As)
51989            );
51990            if is_delimiter {
51991                let alias_token = self.advance();
51992                let alias_name = alias_token.text.clone();
51993                return Expression::Alias(Box::new(crate::expressions::Alias::new(
51994                    expr,
51995                    Identifier::new(alias_name),
51996                )));
51997            }
51998        }
51999        expr
52000    }
52001
52002    /// ClickHouse alias in function arguments: handles both implicit (`expr identifier`)
52003    /// and explicit (`expr AS identifier`) aliases. Use this in special function parsers
52004    /// (SUBSTRING, TRIM, EXTRACT) but NOT in CAST (which has its own AS handling).
52005    /// Normalize TSQL date part aliases (e.g., dd -> DAY, yy -> YEAR, etc.)
52006    fn normalize_tsql_date_part(&self, expr: Expression) -> Expression {
52007        let name = match &expr {
52008            Expression::Var(v) => Some(v.this.to_ascii_uppercase()),
52009            Expression::Column(c) if c.table.is_none() => Some(c.name.name.to_ascii_uppercase()),
52010            Expression::Identifier(id) => Some(id.name.to_ascii_uppercase()),
52011            _ => None,
52012        };
52013        if let Some(name) = name {
52014            let mapped = match name.as_str() {
52015                "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
52016                "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
52017                "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
52018                "DOW" | "DW" | "WEEKDAY" => "DAYOFWEEK",
52019                "DOY" | "DY" | "Y" => "DAYOFYEAR",
52020                "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" => "WEEK",
52021                "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
52022                "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
52023                "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
52024                "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
52025                "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
52026                | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
52027                "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
52028                | "MICROSECONDS" | "MCS" => "MICROSECOND",
52029                "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
52030                "TZH" => "TIMEZONE_HOUR",
52031                "TZM" | "TZOFFSET" | "TZ" => "TIMEZONE_MINUTE",
52032                "DEC" | "DECS" | "DECADES" => "DECADE",
52033                "MIL" | "MILS" | "MILLENIA" => "MILLENNIUM",
52034                "C" | "CENT" | "CENTS" | "CENTURIES" => "CENTURY",
52035                "ISOWK" | "ISOWW" | "ISO_WEEK" | "WEEKOFYEARISO" | "WEEKOFYEAR_ISO"
52036                | "WEEK_ISO" => "WEEKISO",
52037                _ => return expr, // No mapping, return as-is
52038            };
52039            return Expression::Var(Box::new(Var {
52040                this: mapped.to_string(),
52041            }));
52042        }
52043        expr
52044    }
52045
52046    fn try_parse_date_part_unit_expr(&self, expr: &Expression) -> Option<IntervalUnit> {
52047        let upper = self.date_part_expr_name(expr)?.to_ascii_uppercase();
52048        let canonical = match upper.as_str() {
52049            // Year
52050            "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
52051            // Quarter
52052            "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
52053            // Month
52054            "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
52055            // Week
52056            "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" | "WEEKS" => "WEEK",
52057            // Day
52058            "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
52059            // Hour
52060            "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
52061            // Minute
52062            "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
52063            // Second
52064            "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
52065            // Millisecond
52066            "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
52067            | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
52068            // Microsecond
52069            "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
52070            | "MICROSECONDS" | "MCS" => "MICROSECOND",
52071            // Nanosecond
52072            "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
52073            _ => upper.as_str(),
52074        };
52075
52076        Self::parse_interval_unit_from_string(canonical)
52077    }
52078
52079    fn try_parse_date_part_unit_identifier_expr(&self, expr: &Expression) -> Option<IntervalUnit> {
52080        let upper = self
52081            .date_part_identifier_expr_name(expr)?
52082            .to_ascii_uppercase();
52083        let canonical = match upper.as_str() {
52084            "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
52085            "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
52086            "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
52087            "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" | "WEEKS" => "WEEK",
52088            "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
52089            "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
52090            "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
52091            "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
52092            "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
52093            | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
52094            "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
52095            | "MICROSECONDS" | "MCS" => "MICROSECOND",
52096            "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
52097            _ => upper.as_str(),
52098        };
52099
52100        Self::parse_interval_unit_from_string(canonical)
52101    }
52102
52103    fn try_parse_date_part_field_identifier_expr(
52104        &self,
52105        expr: &Expression,
52106    ) -> Option<DateTimeField> {
52107        let upper = self
52108            .date_part_identifier_expr_name(expr)?
52109            .to_ascii_uppercase();
52110        Some(match upper.as_str() {
52111            "YEAR" | "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => DateTimeField::Year,
52112            "MONTH" | "MM" | "MON" | "MONS" | "MONTHS" => DateTimeField::Month,
52113            "DAY" | "D" | "DD" | "DAYS" | "DAYOFMONTH" => DateTimeField::Day,
52114            "HOUR" | "H" | "HH" | "HR" | "HOURS" | "HRS" => DateTimeField::Hour,
52115            "MINUTE" | "MI" | "MIN" | "MINUTES" | "MINS" => DateTimeField::Minute,
52116            "SECOND" | "S" | "SEC" | "SECONDS" | "SECS" => DateTimeField::Second,
52117            "MILLISECOND" | "MS" | "MSEC" | "MILLISECONDS" => DateTimeField::Millisecond,
52118            "MICROSECOND" | "US" | "USEC" | "MICROSECONDS" => DateTimeField::Microsecond,
52119            "DOW" | "DAYOFWEEK" | "DW" => DateTimeField::DayOfWeek,
52120            "DOY" | "DAYOFYEAR" | "DY" => DateTimeField::DayOfYear,
52121            "WEEK" | "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WW" => DateTimeField::Week,
52122            "QUARTER" | "Q" | "QTR" | "QTRS" | "QUARTERS" => DateTimeField::Quarter,
52123            "EPOCH" | "EPOCH_SECOND" | "EPOCH_SECONDS" => DateTimeField::Epoch,
52124            "TIMEZONE" => DateTimeField::Timezone,
52125            "TIMEZONE_HOUR" | "TZH" => DateTimeField::TimezoneHour,
52126            "TIMEZONE_MINUTE" | "TZM" => DateTimeField::TimezoneMinute,
52127            "DATE" => DateTimeField::Date,
52128            "TIME" => DateTimeField::Time,
52129            other => DateTimeField::Custom(other.to_string()),
52130        })
52131    }
52132
52133    fn convert_date_part_identifier_expr_to_var(&self, expr: Expression) -> Expression {
52134        match expr {
52135            Expression::Var(_) => expr,
52136            Expression::Column(c) if c.table.is_none() => {
52137                Expression::Var(Box::new(Var { this: c.name.name }))
52138            }
52139            Expression::Identifier(id) => Expression::Var(Box::new(Var { this: id.name })),
52140            _ => expr,
52141        }
52142    }
52143
52144    fn date_part_identifier_expr_name<'a>(&self, expr: &'a Expression) -> Option<&'a str> {
52145        match expr {
52146            Expression::Var(v) => Some(v.this.as_str()),
52147            Expression::Column(c) if c.table.is_none() => Some(c.name.name.as_str()),
52148            Expression::Identifier(id) => Some(id.name.as_str()),
52149            _ => None,
52150        }
52151    }
52152
52153    fn date_part_expr_name<'a>(&self, expr: &'a Expression) -> Option<&'a str> {
52154        self.date_part_identifier_expr_name(expr).or(match expr {
52155            Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
52156                let Literal::String(s) = lit.as_ref() else {
52157                    unreachable!()
52158                };
52159                Some(s.as_str())
52160            }
52161            _ => None,
52162        })
52163    }
52164
52165    fn try_clickhouse_func_arg_alias(&mut self, expr: Expression) -> Expression {
52166        if !matches!(
52167            self.config.dialect,
52168            Some(crate::dialects::DialectType::ClickHouse)
52169        ) {
52170            return expr;
52171        }
52172        // Try implicit alias first
52173        if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
52174            let next_after = self.peek_nth(1).map(|t| t.token_type);
52175            let is_delimiter = matches!(
52176                next_after,
52177                Some(TokenType::Comma)
52178                    | Some(TokenType::RParen)
52179                    | Some(TokenType::From)
52180                    | Some(TokenType::For)
52181                    | Some(TokenType::As)
52182            );
52183            if is_delimiter {
52184                let alias_token = self.advance();
52185                let alias_name = alias_token.text.clone();
52186                return Expression::Alias(Box::new(crate::expressions::Alias::new(
52187                    expr,
52188                    Identifier::new(alias_name),
52189                )));
52190            }
52191        }
52192        // Try explicit AS alias
52193        if self.check(TokenType::As) {
52194            let next_idx = self.current + 1;
52195            let after_alias_idx = self.current + 2;
52196            let is_alias_token = next_idx < self.tokens.len()
52197                && matches!(
52198                    self.tokens[next_idx].token_type,
52199                    TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
52200                );
52201            let is_delimiter = is_alias_token
52202                && after_alias_idx < self.tokens.len()
52203                && matches!(
52204                    self.tokens[after_alias_idx].token_type,
52205                    TokenType::Comma
52206                        | TokenType::RParen
52207                        | TokenType::From
52208                        | TokenType::For
52209                        | TokenType::As
52210                );
52211            if is_delimiter {
52212                self.skip(); // consume AS
52213                let alias_token = self.advance();
52214                let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
52215                    let mut ident = Identifier::new(alias_token.text.clone());
52216                    ident.quoted = true;
52217                    ident
52218                } else {
52219                    Identifier::new(alias_token.text.clone())
52220                };
52221                return Expression::Alias(Box::new(crate::expressions::Alias::new(
52222                    expr, alias_name,
52223                )));
52224            }
52225        }
52226        expr
52227    }
52228
52229    /// parse_clickhouse_engine_expression - Parse ENGINE expression with optional args
52230    fn parse_clickhouse_engine_expression(&mut self) -> Result<Expression> {
52231        if self.is_at_end() {
52232            return Err(self.parse_error("Expected engine name after ENGINE"));
52233        }
52234
52235        let token = self.advance();
52236        let quoted = matches!(token.token_type, TokenType::QuotedIdentifier);
52237        let name = token.text.clone();
52238
52239        let ident = Expression::Identifier(Identifier {
52240            name,
52241            quoted,
52242            trailing_comments: Vec::new(),
52243            span: None,
52244        });
52245
52246        if self.match_token(TokenType::LParen) {
52247            let args = if self.check(TokenType::RParen) {
52248                Vec::new()
52249            } else {
52250                self.parse_expression_list()?
52251            };
52252            self.expect(TokenType::RParen)?;
52253            Ok(Expression::Anonymous(Box::new(Anonymous {
52254                this: Box::new(ident),
52255                expressions: args,
52256            })))
52257        } else {
52258            Ok(ident)
52259        }
52260    }
52261
52262    /// parse_property_assignment - Ported from Python _parse_property_assignment
52263    /// Parses a property assignment: optionally = or AS, then a value
52264    #[allow(unused_variables, unused_mut)]
52265    pub fn parse_property_assignment(&mut self) -> Result<Option<Expression>> {
52266        // Optionally match = or AS
52267        let _ = self.match_token(TokenType::Eq);
52268        let _ = self.match_token(TokenType::Alias);
52269
52270        // Parse the value as an unquoted field
52271        let value = self.parse_unquoted_field()?;
52272
52273        Ok(value)
52274    }
52275
52276    /// parse_property_before - Implemented from Python _parse_property_before
52277    #[allow(unused_variables, unused_mut)]
52278    pub fn parse_property_before(&mut self) -> Result<Option<Expression>> {
52279        if self.match_text_seq(&["NO"]) {
52280            // Matched: NO
52281            return Ok(None);
52282        }
52283        if self.match_text_seq(&["DUAL"]) {
52284            // Matched: DUAL
52285            return Ok(None);
52286        }
52287        if self.match_text_seq(&["BEFORE"]) {
52288            // Matched: BEFORE
52289            return Ok(None);
52290        }
52291        if self.match_texts(&["MIN", "MINIMUM"]) {
52292            // Matched one of: MIN, MINIMUM
52293            return Ok(None);
52294        }
52295        if self.match_texts(&["MAX", "MAXIMUM"]) {
52296            // Matched one of: MAX, MAXIMUM
52297            return Ok(None);
52298        }
52299        Ok(None)
52300    }
52301
52302    /// parse_qualify - Parse QUALIFY clause (Snowflake, BigQuery)
52303    /// Python: if not self._match(TokenType.QUALIFY): return None; return exp.Qualify(this=self._parse_disjunction())
52304    pub fn parse_qualify(&mut self) -> Result<Option<Expression>> {
52305        if !self.match_token(TokenType::Qualify) {
52306            return Ok(None);
52307        }
52308        let condition = self.parse_expression()?;
52309        Ok(Some(Expression::Qualify(Box::new(Qualify {
52310            this: condition,
52311        }))))
52312    }
52313
52314    /// parse_range - Parses range expressions (BETWEEN, LIKE, IN, IS, etc.)
52315    /// Python: _parse_range
52316    pub fn parse_range(&mut self) -> Result<Option<Expression>> {
52317        // First parse a bitwise expression as the left side
52318        let mut this = self.parse_bitwise()?;
52319        if this.is_none() {
52320            return Ok(None);
52321        }
52322
52323        // Check for NOT (for NOT LIKE, NOT IN, NOT BETWEEN, etc.)
52324        let negate = self.match_token(TokenType::Not);
52325
52326        // BETWEEN
52327        if self.match_token(TokenType::Between) {
52328            let between = self.parse_between_with_expr(this.clone(), negate)?;
52329            this = Some(between);
52330            return Ok(this);
52331        }
52332
52333        // LIKE
52334        if self.match_token(TokenType::Like) {
52335            let left = this.clone().expect("left expression checked above");
52336            let right = self
52337                .parse_bitwise()?
52338                .ok_or_else(|| self.parse_error("Expected expression after LIKE"))?;
52339            let escape = self.parse_escape()?;
52340            let like = Expression::Like(Box::new(LikeOp {
52341                left,
52342                right,
52343                escape,
52344                quantifier: None,
52345                inferred_type: None,
52346            }));
52347            this = if negate {
52348                Some(Expression::Not(Box::new(UnaryOp {
52349                    this: like,
52350                    inferred_type: None,
52351                })))
52352            } else {
52353                Some(like)
52354            };
52355            return Ok(this);
52356        }
52357
52358        // ILIKE
52359        if self.match_token(TokenType::ILike) {
52360            let left = this.clone().expect("left expression checked above");
52361            let right = self
52362                .parse_bitwise()?
52363                .ok_or_else(|| self.parse_error("Expected expression after ILIKE"))?;
52364            let escape = self.parse_escape()?;
52365            let ilike = Expression::ILike(Box::new(LikeOp {
52366                left,
52367                right,
52368                escape,
52369                quantifier: None,
52370                inferred_type: None,
52371            }));
52372            this = if negate {
52373                Some(Expression::Not(Box::new(UnaryOp {
52374                    this: ilike,
52375                    inferred_type: None,
52376                })))
52377            } else {
52378                Some(ilike)
52379            };
52380            return Ok(this);
52381        }
52382
52383        // IN
52384        if self.match_token(TokenType::In) {
52385            let in_expr = self.parse_in_with_expr(this.clone())?;
52386            this = if negate {
52387                Some(Expression::Not(Box::new(UnaryOp {
52388                    this: in_expr,
52389                    inferred_type: None,
52390                })))
52391            } else {
52392                Some(in_expr)
52393            };
52394            return Ok(this);
52395        }
52396
52397        // IS [NOT] NULL / IS [NOT] TRUE / IS [NOT] FALSE
52398        if self.match_token(TokenType::Is) {
52399            let is_expr = self.parse_is_with_expr(this.clone())?;
52400            this = Some(is_expr);
52401            return Ok(this);
52402        }
52403
52404        // Handle standalone NOT with NULL (for NOT NULL pattern after negate)
52405        if negate && self.match_token(TokenType::Null) {
52406            if let Some(left) = this {
52407                let is_null = Expression::Is(Box::new(BinaryOp {
52408                    left,
52409                    right: Expression::Null(Null),
52410                    left_comments: Vec::new(),
52411                    operator_comments: Vec::new(),
52412                    trailing_comments: Vec::new(),
52413                    inferred_type: None,
52414                }));
52415                return Ok(Some(Expression::Not(Box::new(UnaryOp {
52416                    this: is_null,
52417                    inferred_type: None,
52418                }))));
52419            }
52420        }
52421
52422        Ok(this)
52423    }
52424
52425    /// parse_between_with_expr - Parses BETWEEN expression with given left side
52426    fn parse_between_with_expr(
52427        &mut self,
52428        this: Option<Expression>,
52429        negate: bool,
52430    ) -> Result<Expression> {
52431        let this_expr = match this {
52432            Some(e) => e,
52433            None => return Err(self.parse_error("Expected expression before BETWEEN")),
52434        };
52435
52436        // Check for SYMMETRIC/ASYMMETRIC qualifier
52437        let symmetric = if self.match_texts(&["SYMMETRIC"]) {
52438            Some(true)
52439        } else if self.match_texts(&["ASYMMETRIC"]) {
52440            Some(false)
52441        } else {
52442            None
52443        };
52444
52445        let low = self
52446            .parse_bitwise()?
52447            .ok_or_else(|| self.parse_error("Expected low expression after BETWEEN"))?;
52448
52449        if !self.match_token(TokenType::And) {
52450            return Err(self.parse_error("Expected AND in BETWEEN expression"));
52451        }
52452
52453        let high = self
52454            .parse_bitwise()?
52455            .ok_or_else(|| self.parse_error("Expected high expression after AND in BETWEEN"))?;
52456
52457        Ok(Expression::Between(Box::new(Between {
52458            this: this_expr,
52459            low,
52460            high,
52461            not: negate,
52462            symmetric,
52463        })))
52464    }
52465
52466    /// parse_in_with_expr - Parses IN expression with given left side
52467    fn parse_in_with_expr(&mut self, this: Option<Expression>) -> Result<Expression> {
52468        let this_expr = match this {
52469            Some(e) => e,
52470            None => return Err(self.parse_error("Expected expression before IN")),
52471        };
52472
52473        // BigQuery: IN UNNEST(expr) — UNNEST without wrapping parentheses
52474        if self.check_identifier("UNNEST") {
52475            self.skip(); // consume UNNEST
52476            self.expect(TokenType::LParen)?;
52477            let unnest_expr = self.parse_expression()?;
52478            self.expect(TokenType::RParen)?;
52479            return Ok(Expression::In(Box::new(In {
52480                this: this_expr,
52481                expressions: Vec::new(),
52482                query: None,
52483                not: false,
52484                global: false,
52485                unnest: Some(Box::new(unnest_expr)),
52486                is_field: false,
52487            })));
52488        }
52489
52490        // Parse the IN list (subquery or value list)
52491        if !self.match_token(TokenType::LParen) {
52492            // DuckDB: IN without parentheses for array/list membership: 'red' IN tbl.flags
52493            // Try to parse as a single expression (column/array reference)
52494            if let Ok(expr) = self.parse_primary() {
52495                return Ok(Expression::In(Box::new(In {
52496                    this: this_expr,
52497                    expressions: vec![expr],
52498                    query: None,
52499                    not: false,
52500                    global: false,
52501                    unnest: None,
52502                    is_field: true,
52503                })));
52504            }
52505            return Err(self.parse_error("Expected expression or parenthesized list after IN"));
52506        }
52507
52508        // Check if it's a subquery
52509        if self.check(TokenType::Select) {
52510            let subquery = self.parse_select()?;
52511            self.expect(TokenType::RParen)?;
52512            return Ok(Expression::In(Box::new(In {
52513                this: this_expr,
52514                expressions: Vec::new(),
52515                query: Some(subquery),
52516                not: false,
52517                global: false,
52518                unnest: None,
52519                is_field: false,
52520            })));
52521        }
52522
52523        // Parse value list. Pre-size for large IN lists to reduce reallocations.
52524        let capacity_hint = self.estimate_expression_list_capacity_until_rparen();
52525        let expressions = self.parse_expression_list_with_capacity(capacity_hint)?;
52526        self.expect(TokenType::RParen)?;
52527
52528        if expressions.is_empty() {
52529            return Err(self.parse_error("Expected expression list after IN"));
52530        }
52531
52532        Ok(Expression::In(Box::new(In {
52533            this: this_expr,
52534            expressions,
52535            query: None,
52536            not: false,
52537            global: false,
52538            unnest: None,
52539            is_field: false,
52540        })))
52541    }
52542
52543    /// parse_is_with_expr - Parses IS expression with given left side
52544    fn parse_is_with_expr(&mut self, this: Option<Expression>) -> Result<Expression> {
52545        let this_expr = match this {
52546            Some(e) => e,
52547            None => return Err(self.parse_error("Expected expression before IS")),
52548        };
52549
52550        let negate = self.match_token(TokenType::Not);
52551
52552        // IS NULL
52553        if self.match_token(TokenType::Null) {
52554            let is_null = Expression::Is(Box::new(BinaryOp {
52555                left: this_expr,
52556                right: Expression::Null(Null),
52557                left_comments: Vec::new(),
52558                operator_comments: Vec::new(),
52559                trailing_comments: Vec::new(),
52560                inferred_type: None,
52561            }));
52562            return if negate {
52563                Ok(Expression::Not(Box::new(UnaryOp {
52564                    this: is_null,
52565                    inferred_type: None,
52566                })))
52567            } else {
52568                Ok(is_null)
52569            };
52570        }
52571
52572        // IS TRUE
52573        if self.match_texts(&["TRUE"]) {
52574            let is_true = Expression::Is(Box::new(BinaryOp {
52575                left: this_expr,
52576                right: Expression::Boolean(BooleanLiteral { value: true }),
52577                left_comments: Vec::new(),
52578                operator_comments: Vec::new(),
52579                trailing_comments: Vec::new(),
52580                inferred_type: None,
52581            }));
52582            return if negate {
52583                Ok(Expression::Not(Box::new(UnaryOp {
52584                    this: is_true,
52585                    inferred_type: None,
52586                })))
52587            } else {
52588                Ok(is_true)
52589            };
52590        }
52591
52592        // IS FALSE
52593        if self.match_texts(&["FALSE"]) {
52594            let is_false = Expression::Is(Box::new(BinaryOp {
52595                left: this_expr,
52596                right: Expression::Boolean(BooleanLiteral { value: false }),
52597                left_comments: Vec::new(),
52598                operator_comments: Vec::new(),
52599                trailing_comments: Vec::new(),
52600                inferred_type: None,
52601            }));
52602            return if negate {
52603                Ok(Expression::Not(Box::new(UnaryOp {
52604                    this: is_false,
52605                    inferred_type: None,
52606                })))
52607            } else {
52608                Ok(is_false)
52609            };
52610        }
52611
52612        // IS JSON [VALUE|SCALAR|OBJECT|ARRAY] [WITH UNIQUE KEYS|WITHOUT UNIQUE KEYS|UNIQUE KEYS]
52613        if self.match_texts(&["JSON"]) {
52614            // Parse optional JSON type
52615            let json_type = if self.match_texts(&["VALUE"]) {
52616                Some("VALUE".to_string())
52617            } else if self.match_texts(&["SCALAR"]) {
52618                Some("SCALAR".to_string())
52619            } else if self.match_texts(&["OBJECT"]) {
52620                Some("OBJECT".to_string())
52621            } else if self.match_texts(&["ARRAY"]) {
52622                Some("ARRAY".to_string())
52623            } else {
52624                None
52625            };
52626
52627            // Parse optional key uniqueness constraint
52628            let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE", "KEYS"]) {
52629                Some(JsonUniqueKeys::With)
52630            } else if self.match_text_seq(&["WITHOUT", "UNIQUE", "KEYS"]) {
52631                Some(JsonUniqueKeys::Without)
52632            } else if self.match_text_seq(&["UNIQUE", "KEYS"]) {
52633                // Shorthand for WITH UNIQUE KEYS
52634                Some(JsonUniqueKeys::Shorthand)
52635            } else {
52636                None
52637            };
52638
52639            return Ok(Expression::IsJson(Box::new(IsJson {
52640                this: this_expr,
52641                json_type,
52642                unique_keys,
52643                negated: negate,
52644            })));
52645        }
52646
52647        // IS DISTINCT FROM / IS NOT DISTINCT FROM
52648        if self.match_text_seq(&["DISTINCT", "FROM"]) {
52649            let right = self.parse_bitwise()?;
52650            if let Some(right_expr) = right {
52651                // IS DISTINCT FROM is semantically "not equal with null handling"
52652                // Use NullSafeNeq for IS DISTINCT FROM
52653                // If negate was set (IS NOT DISTINCT FROM), use NullSafeEq
52654                let expr = if negate {
52655                    Expression::NullSafeEq(Box::new(BinaryOp {
52656                        left: this_expr,
52657                        right: right_expr,
52658                        left_comments: Vec::new(),
52659                        operator_comments: Vec::new(),
52660                        trailing_comments: Vec::new(),
52661                        inferred_type: None,
52662                    }))
52663                } else {
52664                    Expression::NullSafeNeq(Box::new(BinaryOp {
52665                        left: this_expr,
52666                        right: right_expr,
52667                        left_comments: Vec::new(),
52668                        operator_comments: Vec::new(),
52669                        trailing_comments: Vec::new(),
52670                        inferred_type: None,
52671                    }))
52672                };
52673                return Ok(expr);
52674            }
52675            return Err(self.parse_error("Expected expression after IS DISTINCT FROM"));
52676        }
52677
52678        Err(self.parse_error("Expected NULL, TRUE, FALSE, JSON, or DISTINCT FROM after IS"))
52679    }
52680
52681    /// parse_reads_property - Implemented from Python _parse_reads_property
52682    #[allow(unused_variables, unused_mut)]
52683    pub fn parse_reads_property(&mut self) -> Result<Option<Expression>> {
52684        if self.match_text_seq(&["SQL", "DATA"]) {
52685            // Matched: SQL DATA
52686            return Ok(None);
52687        }
52688        Ok(None)
52689    }
52690
52691    /// parse_recursive_with_search - Parse SEARCH/CYCLE clause for recursive CTEs (PostgreSQL)
52692    /// Syntax: SEARCH BREADTH|DEPTH FIRST BY column SET column [USING column]
52693    ///     or: CYCLE column SET column USING column
52694    #[allow(unused_variables, unused_mut)]
52695    pub fn parse_recursive_with_search(&mut self) -> Result<Option<Box<Expression>>> {
52696        // Check for SEARCH or CYCLE keyword
52697        let kind = if self.match_text_seq(&["SEARCH"]) {
52698            // SEARCH BREADTH|DEPTH FIRST BY ...
52699            let search_kind = if self.match_text_seq(&["BREADTH"]) {
52700                "BREADTH"
52701            } else if self.match_text_seq(&["DEPTH"]) {
52702                "DEPTH"
52703            } else {
52704                return Ok(None);
52705            };
52706            // Consume "FIRST BY"
52707            self.match_text_seq(&["FIRST"]);
52708            self.match_text_seq(&["BY"]);
52709            search_kind.to_string()
52710        } else if self.match_token(TokenType::Cycle) {
52711            "CYCLE".to_string()
52712        } else {
52713            return Ok(None);
52714        };
52715
52716        // Parse the column(s) - for CYCLE this is typically a single column
52717        let this = self.expect_identifier()?;
52718        let this_expr = Expression::Identifier(Identifier::new(this));
52719
52720        // SET column
52721        let expression = if self.match_text_seq(&["SET"]) {
52722            let set_col = self.expect_identifier()?;
52723            Expression::Identifier(Identifier::new(set_col))
52724        } else {
52725            return Err(self.parse_error("Expected SET in CYCLE/SEARCH clause"));
52726        };
52727
52728        // USING column (optional for SEARCH, required for CYCLE)
52729        let using = if self.match_token(TokenType::Using) {
52730            let using_col = self.expect_identifier()?;
52731            Some(Box::new(Expression::Identifier(Identifier::new(using_col))))
52732        } else {
52733            None
52734        };
52735
52736        Ok(Some(Box::new(Expression::RecursiveWithSearch(Box::new(
52737            RecursiveWithSearch {
52738                kind,
52739                this: Box::new(this_expr),
52740                expression: Box::new(expression),
52741                using,
52742            },
52743        )))))
52744    }
52745
52746    /// parse_references - Ported from Python _parse_references
52747    /// Parses REFERENCES clause for foreign key constraints
52748    #[allow(unused_variables, unused_mut)]
52749    pub fn parse_references(&mut self) -> Result<Option<Expression>> {
52750        if !self.match_token(TokenType::References) {
52751            return Ok(None);
52752        }
52753
52754        // Parse referenced table
52755        let this = self.parse_table()?;
52756        if this.is_none() {
52757            return Err(self.parse_error("Expected table name after REFERENCES"));
52758        }
52759
52760        // Parse optional column list (table(col1, col2))
52761        let expressions = if self.match_token(TokenType::LParen) {
52762            let cols = self.parse_identifier_list()?;
52763            self.expect(TokenType::RParen)?;
52764            cols.into_iter()
52765                .map(|id| Expression::Identifier(id))
52766                .collect()
52767        } else {
52768            Vec::new()
52769        };
52770
52771        // Parse optional constraint options (ON DELETE, ON UPDATE, etc.)
52772        let options = self.parse_fk_constraint_options()?;
52773
52774        Ok(Some(Expression::Reference(Box::new(Reference {
52775            this: Box::new(this.unwrap()),
52776            expressions,
52777            options,
52778        }))))
52779    }
52780
52781    /// Parse key constraint options (ON DELETE CASCADE, ON UPDATE SET NULL, etc.)
52782    fn parse_fk_constraint_options(&mut self) -> Result<Vec<Expression>> {
52783        let mut options = Vec::new();
52784
52785        while self.match_token(TokenType::On) {
52786            let kind = if self.match_token(TokenType::Delete) {
52787                "DELETE"
52788            } else if self.match_token(TokenType::Update) {
52789                "UPDATE"
52790            } else {
52791                break;
52792            };
52793
52794            let action = if self.match_text_seq(&["NO", "ACTION"]) {
52795                "NO ACTION"
52796            } else if self.match_text_seq(&["SET", "NULL"]) {
52797                "SET NULL"
52798            } else if self.match_text_seq(&["SET", "DEFAULT"]) {
52799                "SET DEFAULT"
52800            } else if self.match_token(TokenType::Cascade) {
52801                "CASCADE"
52802            } else if self.match_token(TokenType::Restrict) {
52803                "RESTRICT"
52804            } else {
52805                continue;
52806            };
52807
52808            // Store as simple identifier with the full action description
52809            options.push(Expression::Identifier(Identifier {
52810                name: format!("ON {} {}", kind, action),
52811                quoted: false,
52812                trailing_comments: Vec::new(),
52813                span: None,
52814            }));
52815        }
52816
52817        // Parse MATCH option
52818        if self.match_token(TokenType::Match) {
52819            let match_type = if self.match_identifier("FULL") {
52820                "FULL"
52821            } else if self.match_identifier("PARTIAL") {
52822                "PARTIAL"
52823            } else if self.match_identifier("SIMPLE") {
52824                "SIMPLE"
52825            } else {
52826                ""
52827            };
52828            if !match_type.is_empty() {
52829                options.push(Expression::Identifier(Identifier {
52830                    name: format!("MATCH {}", match_type),
52831                    quoted: false,
52832                    trailing_comments: Vec::new(),
52833                    span: None,
52834                }));
52835            }
52836        }
52837
52838        Ok(options)
52839    }
52840
52841    /// parse_refresh - Implemented from Python _parse_refresh
52842    #[allow(unused_variables, unused_mut)]
52843    /// parse_refresh - Parses REFRESH TABLE or REFRESH MATERIALIZED VIEW
52844    /// Python: parser.py:7656-7668
52845    pub fn parse_refresh(&mut self) -> Result<Option<Expression>> {
52846        let kind = if self.match_token(TokenType::Table) {
52847            "TABLE".to_string()
52848        } else if self.match_text_seq(&["MATERIALIZED", "VIEW"]) {
52849            "MATERIALIZED VIEW".to_string()
52850        } else {
52851            String::new()
52852        };
52853
52854        // Parse the object name (string literal or table name)
52855        // First try a string literal, then fall back to table reference
52856        if let Some(s) = self.parse_string()? {
52857            return Ok(Some(Expression::Refresh(Box::new(Refresh {
52858                this: Box::new(s),
52859                kind,
52860            }))));
52861        }
52862
52863        // Parse as a table reference (schema.table format)
52864        let table_ref = self.parse_table_ref()?;
52865        let table_expr = Expression::Table(Box::new(table_ref));
52866
52867        Ok(Some(Expression::Refresh(Box::new(Refresh {
52868            this: Box::new(table_expr),
52869            kind,
52870        }))))
52871    }
52872
52873    /// parse_refresh_trigger_property - Doris REFRESH clause for materialized views
52874    /// Syntax: REFRESH method ON kind [EVERY n UNIT] [STARTS 'datetime']
52875    /// Examples:
52876    ///   REFRESH COMPLETE ON MANUAL
52877    ///   REFRESH AUTO ON COMMIT
52878    ///   REFRESH AUTO ON SCHEDULE EVERY 5 MINUTE STARTS '2025-01-01 00:00:00'
52879    pub fn parse_refresh_trigger_property(&mut self) -> Result<RefreshTriggerProperty> {
52880        // Parse method: COMPLETE or AUTO
52881        let method = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
52882
52883        // Parse ON
52884        self.expect(TokenType::On)?;
52885
52886        // Parse kind: MANUAL, COMMIT, or SCHEDULE
52887        let kind_text = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
52888        let kind = Some(kind_text.clone());
52889
52890        // For SCHEDULE, parse EVERY n UNIT [STARTS 'datetime']
52891        let (every, unit, starts) = if kind_text == "SCHEDULE" {
52892            // EVERY n UNIT
52893            let every = if self.match_identifier("EVERY") {
52894                // parse_number returns Option<Expression> with Expression::Literal(Box::new(Literal::Number(...)))
52895                self.parse_number()?.map(Box::new)
52896            } else {
52897                None
52898            };
52899
52900            // Unit: MINUTE, HOUR, DAY, etc.
52901            let unit = if every.is_some() {
52902                Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase())
52903            } else {
52904                None
52905            };
52906
52907            // STARTS 'datetime'
52908            let starts = if self.match_identifier("STARTS") {
52909                let s = self.expect_string()?;
52910                Some(Box::new(Expression::Literal(Box::new(Literal::String(s)))))
52911            } else {
52912                None
52913            };
52914
52915            (every, unit, starts)
52916        } else {
52917            (None, None, None)
52918        };
52919
52920        Ok(RefreshTriggerProperty {
52921            method,
52922            kind,
52923            every,
52924            unit,
52925            starts,
52926        })
52927    }
52928
52929    /// parse_remote_with_connection - Implemented from Python _parse_remote_with_connection
52930    #[allow(unused_variables, unused_mut)]
52931    pub fn parse_remote_with_connection(&mut self) -> Result<Option<Expression>> {
52932        if self.match_text_seq(&["WITH", "CONNECTION"]) {
52933            // Matched: WITH CONNECTION
52934            return Ok(None);
52935        }
52936        Ok(None)
52937    }
52938
52939    /// parse_respect_or_ignore_nulls - Implemented from Python _parse_respect_or_ignore_nulls
52940    #[allow(unused_variables, unused_mut)]
52941    pub fn parse_respect_or_ignore_nulls(&mut self) -> Result<Option<Expression>> {
52942        if self.match_text_seq(&["IGNORE", "NULLS"]) {
52943            // Matched: IGNORE NULLS
52944            return Ok(None);
52945        }
52946        if self.match_text_seq(&["RESPECT", "NULLS"]) {
52947            // Matched: RESPECT NULLS
52948            return Ok(None);
52949        }
52950        Ok(None)
52951    }
52952
52953    /// parse_retention_period - Parses HISTORY_RETENTION_PERIOD (TSQL)
52954    /// Python: _parse_retention_period
52955    /// Format: INFINITE | <number> DAY | DAYS | MONTH | MONTHS | YEAR | YEARS
52956    pub fn parse_retention_period(&mut self) -> Result<Option<Expression>> {
52957        // Try to parse a number first
52958        let number = self.parse_number()?;
52959        let number_str = number
52960            .map(|n| match n {
52961                Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
52962                    let Literal::Number(s) = lit.as_ref() else {
52963                        unreachable!()
52964                    };
52965                    format!("{} ", s)
52966                }
52967                _ => String::new(),
52968            })
52969            .unwrap_or_default();
52970
52971        // Parse the unit (any token as a variable)
52972        let unit = self.parse_var_any_token()?;
52973        let unit_str = unit
52974            .map(|u| match u {
52975                Expression::Var(v) => v.this.clone(),
52976                _ => String::new(),
52977            })
52978            .unwrap_or_default();
52979
52980        let result = format!("{}{}", number_str, unit_str);
52981        Ok(Some(Expression::Var(Box::new(Var { this: result }))))
52982    }
52983
52984    /// parse_var_any_token - Parses any token as a Var (for flexible parsing)
52985    fn parse_var_any_token(&mut self) -> Result<Option<Expression>> {
52986        if !self.is_at_end() {
52987            let token = self.advance();
52988            Ok(Some(Expression::Var(Box::new(Var {
52989                this: token.text.clone(),
52990            }))))
52991        } else {
52992            Ok(None)
52993        }
52994    }
52995
52996    /// parse_returning - Creates Returning expression
52997    /// Parses RETURNING clause (PostgreSQL) for INSERT/UPDATE/DELETE
52998    #[allow(unused_variables, unused_mut)]
52999    pub fn parse_returning(&mut self) -> Result<Option<Expression>> {
53000        if !self.match_token(TokenType::Returning) {
53001            return Ok(None);
53002        }
53003
53004        // Parse expressions (column list or *)
53005        let expressions = self.parse_expression_list()?;
53006
53007        // Check for INTO target_table (Oracle style)
53008        let into = if self.match_token(TokenType::Into) {
53009            self.parse_table()?.map(Box::new)
53010        } else {
53011            None
53012        };
53013
53014        Ok(Some(Expression::Returning(Box::new(Returning {
53015            expressions,
53016            into,
53017        }))))
53018    }
53019
53020    /// parse_output_clause - Parses OUTPUT clause (TSQL)
53021    /// Used in INSERT/UPDATE/DELETE and MERGE statements
53022    /// Supports expressions with optional AS aliases: OUTPUT col1, col2 AS alias, col3
53023    pub fn parse_output_clause(&mut self) -> Result<OutputClause> {
53024        // Parse comma-separated list of columns/expressions with optional aliases
53025        let mut columns = Vec::new();
53026        loop {
53027            let expr = self.parse_expression()?;
53028            // Check for optional AS alias
53029            let expr = if self.match_token(TokenType::As) {
53030                let alias = self.expect_identifier_or_keyword_with_quoted()?;
53031                Expression::Alias(Box::new(Alias {
53032                    this: expr,
53033                    alias,
53034                    column_aliases: Vec::new(),
53035                    pre_alias_comments: Vec::new(),
53036                    trailing_comments: Vec::new(),
53037                    inferred_type: None,
53038                }))
53039            } else {
53040                expr
53041            };
53042            columns.push(expr);
53043            if !self.match_token(TokenType::Comma) {
53044                break;
53045            }
53046        }
53047
53048        // Check for INTO target
53049        let into_table = if self.match_token(TokenType::Into) {
53050            Some(self.parse_expression()?)
53051        } else {
53052            None
53053        };
53054
53055        Ok(OutputClause {
53056            columns,
53057            into_table,
53058        })
53059    }
53060
53061    /// parse_returns - Implemented from Python _parse_returns
53062    /// Calls: parse_types
53063    #[allow(unused_variables, unused_mut)]
53064    pub fn parse_returns(&mut self) -> Result<Option<Expression>> {
53065        if self.match_text_seq(&["NULL", "ON", "NULL", "INPUT"]) {
53066            return Ok(Some(Expression::Schema(Box::new(Schema {
53067                this: None,
53068                expressions: Vec::new(),
53069            }))));
53070        }
53071        Ok(None)
53072    }
53073
53074    /// parse_row - Parses ROW FORMAT clause
53075    /// Returns RowFormatSerdeProperty or RowFormatDelimitedProperty
53076    pub fn parse_row(&mut self) -> Result<Option<Expression>> {
53077        // Python: if not self._match(TokenType.FORMAT): return None
53078        if !self.match_token(TokenType::Format) {
53079            return Ok(None);
53080        }
53081        self.parse_row_format()
53082    }
53083
53084    /// parse_row_format - Implemented from Python _parse_row_format
53085    /// Parses SERDE or DELIMITED row format specifications
53086    pub fn parse_row_format(&mut self) -> Result<Option<Expression>> {
53087        // Check for SERDE row format
53088        if self.match_text_seq(&["SERDE"]) {
53089            let this = self.parse_string()?;
53090            let serde_properties = self.parse_serde_properties(false)?;
53091
53092            return Ok(Some(Expression::RowFormatSerdeProperty(Box::new(
53093                RowFormatSerdeProperty {
53094                    this: Box::new(this.unwrap_or(Expression::Null(Null))),
53095                    serde_properties: serde_properties.map(Box::new),
53096                },
53097            ))));
53098        }
53099
53100        // Check for DELIMITED row format
53101        self.match_text_seq(&["DELIMITED"]);
53102
53103        let mut fields = None;
53104        let mut escaped = None;
53105        let mut collection_items = None;
53106        let mut map_keys = None;
53107        let mut lines = None;
53108        let mut null = None;
53109
53110        // Parse FIELDS TERMINATED BY
53111        if self.match_text_seq(&["FIELDS", "TERMINATED", "BY"]) {
53112            fields = self.parse_string()?.map(Box::new);
53113            // Parse optional ESCAPED BY
53114            if self.match_text_seq(&["ESCAPED", "BY"]) {
53115                escaped = self.parse_string()?.map(Box::new);
53116            }
53117        }
53118
53119        // Parse COLLECTION ITEMS TERMINATED BY
53120        if self.match_text_seq(&["COLLECTION", "ITEMS", "TERMINATED", "BY"]) {
53121            collection_items = self.parse_string()?.map(Box::new);
53122        }
53123
53124        // Parse MAP KEYS TERMINATED BY
53125        if self.match_text_seq(&["MAP", "KEYS", "TERMINATED", "BY"]) {
53126            map_keys = self.parse_string()?.map(Box::new);
53127        }
53128
53129        // Parse LINES TERMINATED BY
53130        if self.match_text_seq(&["LINES", "TERMINATED", "BY"]) {
53131            lines = self.parse_string()?.map(Box::new);
53132        }
53133
53134        // Parse NULL DEFINED AS
53135        if self.match_text_seq(&["NULL", "DEFINED", "AS"]) {
53136            null = self.parse_string()?.map(Box::new);
53137        }
53138
53139        // Parse optional WITH SERDEPROPERTIES
53140        let serde = self.parse_serde_properties(false)?.map(Box::new);
53141
53142        Ok(Some(Expression::RowFormatDelimitedProperty(Box::new(
53143            RowFormatDelimitedProperty {
53144                fields,
53145                escaped,
53146                collection_items,
53147                map_keys,
53148                lines,
53149                null,
53150                serde,
53151            },
53152        ))))
53153    }
53154
53155    /// parse_schema - Ported from Python _parse_schema
53156    /// Parses schema definition: (col1 type1, col2 type2, ...)
53157    /// Used for CREATE TABLE column definitions
53158    #[allow(unused_variables, unused_mut)]
53159    pub fn parse_schema(&mut self) -> Result<Option<Expression>> {
53160        self.parse_schema_with_this(None)
53161    }
53162
53163    /// parse_schema_with_this - Parses schema with optional table reference
53164    fn parse_schema_with_this(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
53165        // Check for opening parenthesis
53166        if !self.match_token(TokenType::LParen) {
53167            return Ok(this.map(|e| e));
53168        }
53169
53170        // Check if this is a subquery (SELECT, WITH, etc.) not a schema
53171        if self.check(TokenType::Select) || self.check(TokenType::With) {
53172            // Retreat - put back the LParen
53173            self.current -= 1;
53174            return Ok(this.map(|e| e));
53175        }
53176
53177        // Parse column definitions and constraints
53178        let mut expressions = Vec::new();
53179        if !self.check(TokenType::RParen) {
53180            loop {
53181                // Try to parse constraint first, then field definition
53182                if let Some(constraint) = self.parse_constraint()? {
53183                    expressions.push(constraint);
53184                } else if let Some(field_def) = self.parse_field_def()? {
53185                    expressions.push(field_def);
53186                } else {
53187                    break;
53188                }
53189
53190                if !self.match_token(TokenType::Comma) {
53191                    break;
53192                }
53193            }
53194        }
53195
53196        self.expect(TokenType::RParen)?;
53197
53198        Ok(Some(Expression::Schema(Box::new(Schema {
53199            this: this.map(Box::new),
53200            expressions,
53201        }))))
53202    }
53203
53204    /// Parse schema identifier: name or name(columns)
53205    /// Used for TSQL ON filegroup (partition_column) syntax
53206    fn parse_schema_identifier(&mut self) -> Result<Expression> {
53207        // Parse the identifier (filegroup name)
53208        let name = self.expect_identifier_with_quoted()?;
53209        let name_expr = Expression::Identifier(name);
53210
53211        // Check for optional parenthesized columns
53212        if self.match_token(TokenType::LParen) {
53213            let mut columns = Vec::new();
53214            loop {
53215                let col = self.expect_identifier_with_quoted()?;
53216                columns.push(Expression::Identifier(col));
53217                if !self.match_token(TokenType::Comma) {
53218                    break;
53219                }
53220            }
53221            self.expect(TokenType::RParen)?;
53222            Ok(Expression::Schema(Box::new(Schema {
53223                this: Some(Box::new(name_expr)),
53224                expressions: columns,
53225            })))
53226        } else {
53227            // Just the identifier, no columns
53228            Ok(name_expr)
53229        }
53230    }
53231
53232    /// parse_security - Implemented from Python _parse_security
53233    #[allow(unused_variables, unused_mut)]
53234    pub fn parse_security(&mut self) -> Result<Option<Expression>> {
53235        if self.match_texts(&["NONE", "DEFINER", "INVOKER"]) {
53236            // Matched one of: NONE, DEFINER, INVOKER
53237            return Ok(None);
53238        }
53239        Ok(None)
53240    }
53241
53242    /// parse_select_or_expression - Parses either a SELECT statement or an expression
53243    /// Python: _parse_select_or_expression
53244    pub fn parse_select_or_expression(&mut self) -> Result<Option<Expression>> {
53245        // Save position for potential backtracking
53246        let start_pos = self.current;
53247
53248        // First try to parse a SELECT statement if we're at a SELECT keyword
53249        if self.check(TokenType::Select) {
53250            return Ok(Some(self.parse_select()?));
53251        }
53252
53253        // Otherwise try to parse an expression (assignment)
53254        if let Some(expr) = self.parse_disjunction()? {
53255            return Ok(Some(expr));
53256        }
53257
53258        // Backtrack if nothing worked
53259        self.current = start_pos;
53260
53261        Ok(None)
53262    }
53263
53264    /// parse_select_query - Implemented from Python _parse_select_query
53265    /// Calls: parse_string, parse_table, parse_describe
53266    #[allow(unused_variables, unused_mut)]
53267    pub fn parse_select_query(&mut self) -> Result<Option<Expression>> {
53268        if self.match_texts(&["STRUCT", "VALUE"]) {
53269            // Matched one of: STRUCT, VALUE
53270            return Ok(None);
53271        }
53272        Ok(None)
53273    }
53274
53275    /// parse_sequence_properties - Implemented from Python _parse_sequence_properties
53276    /// Calls: parse_number, parse_term, parse_column
53277    #[allow(unused_variables, unused_mut)]
53278    pub fn parse_sequence_properties(&mut self) -> Result<Option<Expression>> {
53279        if self.match_text_seq(&["INCREMENT"]) {
53280            return Ok(Some(Expression::SequenceProperties(Box::new(
53281                SequenceProperties {
53282                    increment: None,
53283                    minvalue: None,
53284                    maxvalue: None,
53285                    cache: None,
53286                    start: None,
53287                    owned: None,
53288                    options: Vec::new(),
53289                },
53290            ))));
53291        }
53292        if self.match_text_seq(&["BY"]) {
53293            // Matched: BY
53294            return Ok(None);
53295        }
53296        if self.match_text_seq(&["="]) {
53297            // Matched: =
53298            return Ok(None);
53299        }
53300        Ok(None)
53301    }
53302
53303    /// parse_serde_properties - Implemented from Python _parse_serde_properties
53304    /// Parses SERDEPROPERTIES clause: [WITH] SERDEPROPERTIES (key=value, ...)
53305    pub fn parse_serde_properties(&mut self, with_: bool) -> Result<Option<Expression>> {
53306        let start_index = self.current;
53307        let has_with = with_ || self.match_text_seq(&["WITH"]);
53308
53309        // Check for SERDEPROPERTIES keyword
53310        if !self.match_token(TokenType::SerdeProperties) {
53311            self.current = start_index;
53312            return Ok(None);
53313        }
53314
53315        // Parse wrapped properties manually since parse_property doesn't handle 'key'='value' syntax
53316        let mut expressions = Vec::new();
53317        if self.match_token(TokenType::LParen) {
53318            loop {
53319                if self.check(TokenType::RParen) {
53320                    break;
53321                }
53322                // Parse 'key'='value' or key=value
53323                let key = self.parse_primary()?;
53324                if self.match_token(TokenType::Eq) {
53325                    let value = self.parse_primary()?;
53326                    expressions.push(Expression::Eq(Box::new(BinaryOp::new(key, value))));
53327                } else {
53328                    expressions.push(key);
53329                }
53330                if !self.match_token(TokenType::Comma) {
53331                    break;
53332                }
53333            }
53334            self.expect(TokenType::RParen)?;
53335        }
53336
53337        Ok(Some(Expression::SerdeProperties(Box::new(
53338            SerdeProperties {
53339                expressions,
53340                with_: if has_with {
53341                    Some(Box::new(Expression::Boolean(BooleanLiteral {
53342                        value: true,
53343                    })))
53344                } else {
53345                    None
53346                },
53347            },
53348        ))))
53349    }
53350
53351    /// parse_session_parameter - Ported from Python _parse_session_parameter
53352    #[allow(unused_variables, unused_mut)]
53353    /// parse_session_parameter - Parses session parameters (@@var or @@session.var)
53354    /// Example: @@session.sql_mode, @@global.autocommit
53355    pub fn parse_session_parameter(&mut self) -> Result<Option<Expression>> {
53356        // Parse the first identifier or primary
53357        let first = if let Some(id) = self.parse_id_var()? {
53358            id
53359        } else if let Some(primary) = self.parse_primary_or_var()? {
53360            primary
53361        } else {
53362            return Ok(None);
53363        };
53364
53365        // Check for dot notation (kind.name)
53366        let (kind, this) = if self.match_token(TokenType::Dot) {
53367            // kind is the first part, parse the second
53368            let kind_name = match &first {
53369                Expression::Identifier(id) => Some(id.name.clone()),
53370                _ => None,
53371            };
53372            let second = self
53373                .parse_var()?
53374                .or_else(|| self.parse_primary_or_var().ok().flatten());
53375            (kind_name, second.unwrap_or(first))
53376        } else {
53377            (None, first)
53378        };
53379
53380        Ok(Some(Expression::SessionParameter(Box::new(
53381            SessionParameter {
53382                this: Box::new(this),
53383                kind,
53384            },
53385        ))))
53386    }
53387
53388    /// parse_set_item - Ported from Python _parse_set_item
53389    /// Parses an item in a SET statement (GLOBAL, LOCAL, SESSION prefixes, or assignment)
53390    #[allow(unused_variables, unused_mut)]
53391    pub fn parse_set_item(&mut self) -> Result<Option<Expression>> {
53392        // Check for specific prefixes
53393        let kind = if self.match_text_seq(&["GLOBAL"]) {
53394            Some("GLOBAL".to_string())
53395        } else if self.match_text_seq(&["LOCAL"]) {
53396            Some("LOCAL".to_string())
53397        } else if self.match_text_seq(&["SESSION"]) {
53398            Some("SESSION".to_string())
53399        } else {
53400            None
53401        };
53402
53403        // Delegate to set_item_assignment
53404        self.parse_set_item_assignment()
53405    }
53406
53407    /// parse_set_item_assignment - Implemented from Python _parse_set_item_assignment
53408    /// Parses SET variable = value assignments
53409    pub fn parse_set_item_assignment(&mut self) -> Result<Option<Expression>> {
53410        let start_index = self.current;
53411
53412        // Try to parse as TRANSACTION
53413        if self.match_text_seq(&["TRANSACTION"]) {
53414            // This is handled by parse_set_transaction
53415            return Ok(Some(Expression::SetItem(Box::new(SetItem {
53416                name: Expression::Var(Box::new(Var {
53417                    this: "TRANSACTION".to_string(),
53418                })),
53419                value: Expression::Null(Null),
53420                kind: None,
53421                no_equals: false,
53422            }))));
53423        }
53424
53425        // Parse left side: primary or column
53426        let left = self
53427            .parse_primary_or_var()?
53428            .or_else(|| self.parse_column().ok().flatten());
53429
53430        if left.is_none() {
53431            self.current = start_index;
53432            return Ok(None);
53433        }
53434
53435        // Check for assignment delimiter (= or TO or :=)
53436        if !self.match_texts(&["=", "TO", ":="]) {
53437            self.current = start_index;
53438            return Ok(None);
53439        }
53440
53441        // Parse right side: value
53442        // First try string literals (preserve quoting), then booleans/numbers, then identifiers
53443        let right_val = if self.check(TokenType::String) {
53444            let text = self.advance().text.clone();
53445            Expression::Literal(Box::new(Literal::String(text)))
53446        } else if self.check(TokenType::False) {
53447            self.skip();
53448            Expression::Boolean(BooleanLiteral { value: false })
53449        } else if self.check(TokenType::True) {
53450            self.skip();
53451            Expression::Boolean(BooleanLiteral { value: true })
53452        } else {
53453            let right = self
53454                .parse_id_var()?
53455                .or_else(|| self.parse_primary_or_var().ok().flatten());
53456            // Convert Column/Identifier to Var
53457            match right {
53458                Some(Expression::Column(col)) => Expression::Var(Box::new(Var {
53459                    this: col.name.name.clone(),
53460                })),
53461                Some(Expression::Identifier(id)) => Expression::Var(Box::new(Var {
53462                    this: id.name.clone(),
53463                })),
53464                Some(other) => other,
53465                None => Expression::Null(Null),
53466            }
53467        };
53468
53469        Ok(Some(Expression::SetItem(Box::new(SetItem {
53470            name: left
53471                .ok_or_else(|| self.parse_error("Expected variable name in SET statement"))?,
53472            value: right_val,
53473            kind: None,
53474            no_equals: false,
53475        }))))
53476    }
53477
53478    /// parse_set_operations - Parses UNION/INTERSECT/EXCEPT operations
53479    /// This version parses from current position (expects to be at set operator)
53480    /// Python: _parse_set_operations
53481    pub fn parse_set_operations(&mut self) -> Result<Option<Expression>> {
53482        // Parse a SELECT or subquery first
53483        let left = if self.check(TokenType::Select) {
53484            Some(self.parse_select()?)
53485        } else if self.match_token(TokenType::LParen) {
53486            let inner = self.parse_select()?;
53487            self.match_token(TokenType::RParen);
53488            Some(inner)
53489        } else {
53490            None
53491        };
53492
53493        if left.is_none() {
53494            return Ok(None);
53495        }
53496
53497        self.parse_set_operations_with_expr(left)
53498    }
53499
53500    /// parse_set_operations_with_expr - Parses set operations with a left expression
53501    pub fn parse_set_operations_with_expr(
53502        &mut self,
53503        this: Option<Expression>,
53504    ) -> Result<Option<Expression>> {
53505        let mut result = this;
53506
53507        while result.is_some() {
53508            if let Some(setop) = self.parse_set_operation_with_expr(result.clone())? {
53509                result = Some(setop);
53510            } else {
53511                break;
53512            }
53513        }
53514
53515        Ok(result)
53516    }
53517
53518    /// parse_set_operation_with_expr - Parses a single set operation (UNION, INTERSECT, EXCEPT)
53519    fn parse_set_operation_with_expr(
53520        &mut self,
53521        left: Option<Expression>,
53522    ) -> Result<Option<Expression>> {
53523        let left_expr = match left {
53524            Some(e) => e,
53525            None => return Ok(None),
53526        };
53527
53528        // Check for UNION, INTERSECT, EXCEPT
53529        let op_type = if self.match_token(TokenType::Union) {
53530            "UNION"
53531        } else if self.match_token(TokenType::Intersect) {
53532            "INTERSECT"
53533        } else if self.match_token(TokenType::Except) {
53534            "EXCEPT"
53535        } else {
53536            return Ok(Some(left_expr));
53537        };
53538
53539        // Check for ALL or DISTINCT
53540        let (all, distinct) = if self.match_token(TokenType::All) {
53541            (true, false)
53542        } else {
53543            let d = self.match_token(TokenType::Distinct);
53544            (false, d)
53545        };
53546
53547        // DuckDB: UNION [ALL] BY NAME SELECT ...
53548        let by_name = self.match_token(TokenType::By) && self.match_identifier("NAME");
53549
53550        // Parse the right side (SELECT or subquery)
53551        let right = if self.check(TokenType::Select) {
53552            self.parse_select()?
53553        } else if self.match_token(TokenType::LParen) {
53554            let inner = self.parse_select()?;
53555            self.match_token(TokenType::RParen);
53556            inner
53557        } else {
53558            return Ok(Some(left_expr));
53559        };
53560
53561        // Create the appropriate set operation expression
53562        match op_type {
53563            "UNION" => Ok(Some(Expression::Union(Box::new(Union {
53564                left: left_expr,
53565                right,
53566                all,
53567                distinct,
53568                with: None,
53569                order_by: None,
53570                limit: None,
53571                offset: None,
53572                distribute_by: None,
53573                sort_by: None,
53574                cluster_by: None,
53575                by_name,
53576                side: None,
53577                kind: None,
53578                corresponding: false,
53579                strict: false,
53580                on_columns: Vec::new(),
53581            })))),
53582            "INTERSECT" => Ok(Some(Expression::Intersect(Box::new(Intersect {
53583                left: left_expr,
53584                right,
53585                all,
53586                distinct,
53587                with: None,
53588                order_by: None,
53589                limit: None,
53590                offset: None,
53591                distribute_by: None,
53592                sort_by: None,
53593                cluster_by: None,
53594                by_name,
53595                side: None,
53596                kind: None,
53597                corresponding: false,
53598                strict: false,
53599                on_columns: Vec::new(),
53600            })))),
53601            "EXCEPT" => Ok(Some(Expression::Except(Box::new(Except {
53602                left: left_expr,
53603                right,
53604                all,
53605                distinct,
53606                with: None,
53607                order_by: None,
53608                limit: None,
53609                offset: None,
53610                distribute_by: None,
53611                sort_by: None,
53612                cluster_by: None,
53613                by_name,
53614                side: None,
53615                kind: None,
53616                corresponding: false,
53617                strict: false,
53618                on_columns: Vec::new(),
53619            })))),
53620            _ => Ok(Some(left_expr)),
53621        }
53622    }
53623
53624    /// parse_set_transaction - Implemented from Python _parse_set_transaction
53625    #[allow(unused_variables, unused_mut)]
53626    pub fn parse_set_transaction(&mut self) -> Result<Option<Expression>> {
53627        if self.match_text_seq(&["TRANSACTION"]) {
53628            // Matched: TRANSACTION
53629            return Ok(None);
53630        }
53631        Ok(None)
53632    }
53633
53634    /// Helper to consume an optional ClickHouse SETTINGS clause
53635    /// Used in SHOW, CHECK TABLE, and other ClickHouse statements
53636    fn parse_clickhouse_settings_clause(&mut self) -> Result<()> {
53637        if self.match_token(TokenType::Settings) {
53638            let _ = self.parse_settings_property()?;
53639        }
53640        Ok(())
53641    }
53642
53643    /// parse_settings_property - Parses SETTINGS property (ClickHouse)
53644    /// Python: _parse_settings_property
53645    /// Format: SETTINGS key=value, key=value, ...
53646    pub fn parse_settings_property(&mut self) -> Result<Option<Expression>> {
53647        // Parse comma-separated assignment expressions
53648        let mut expressions = Vec::new();
53649        loop {
53650            if let Some(assignment) = self.parse_assignment()? {
53651                expressions.push(assignment);
53652            } else {
53653                break;
53654            }
53655            if !self.match_token(TokenType::Comma) {
53656                break;
53657            }
53658        }
53659
53660        Ok(Some(Expression::SettingsProperty(Box::new(
53661            SettingsProperty { expressions },
53662        ))))
53663    }
53664
53665    /// parse_simplified_pivot - Ported from Python _parse_simplified_pivot
53666    /// Handles DuckDB simplified PIVOT/UNPIVOT syntax:
53667    ///   PIVOT table ON columns [IN (...)] USING agg_func [AS alias], ... [GROUP BY ...]
53668    ///   UNPIVOT table ON columns [INTO NAME col VALUE col, ...]
53669    #[allow(unused_variables, unused_mut)]
53670    pub fn parse_simplified_pivot(&mut self, is_unpivot: bool) -> Result<Option<Expression>> {
53671        // Parse the source table (can be a subquery like (SELECT 1 AS col1, 2 AS col2))
53672        let this = if self.check(TokenType::LParen) {
53673            // Could be parenthesized subquery
53674            self.skip(); // consume (
53675            if self.check(TokenType::Select) || self.check(TokenType::With) {
53676                let inner = self.parse_statement()?;
53677                self.expect(TokenType::RParen)?;
53678                Some(Expression::Subquery(Box::new(Subquery {
53679                    this: inner,
53680                    alias: None,
53681                    column_aliases: Vec::new(),
53682                    order_by: None,
53683                    limit: None,
53684                    offset: None,
53685                    lateral: false,
53686                    modifiers_inside: false,
53687                    trailing_comments: Vec::new(),
53688                    distribute_by: None,
53689                    sort_by: None,
53690                    cluster_by: None,
53691                    inferred_type: None,
53692                })))
53693            } else {
53694                // Not a subquery, retreat and parse as expression in parens
53695                self.current -= 1; // un-consume the (
53696                Some(self.parse_primary()?)
53697            }
53698        } else {
53699            // Parse table reference (e.g., Cities, schema.table, duckdb_functions())
53700            Some(self.parse_primary()?)
53701        };
53702
53703        // Parse ON columns
53704        let expressions = if self.match_text_seq(&["ON"]) {
53705            let mut on_exprs = Vec::new();
53706            loop {
53707                // Parse ON expression - use parse_bitwise to handle complex expressions like Country || '_' || Name
53708                let on_expr = self.parse_bitwise()?;
53709                if on_expr.is_none() {
53710                    break;
53711                }
53712                let mut expr = on_expr.unwrap();
53713
53714                // Check for IN clause on this column
53715                if self.match_token(TokenType::In) {
53716                    if self.match_token(TokenType::LParen) {
53717                        let mut in_exprs = Vec::new();
53718                        loop {
53719                            if self.check(TokenType::RParen) {
53720                                break;
53721                            }
53722                            if let Some(val) = self.parse_select_or_expression()? {
53723                                in_exprs.push(val);
53724                            }
53725                            if !self.match_token(TokenType::Comma) {
53726                                break;
53727                            }
53728                        }
53729                        self.expect(TokenType::RParen)?;
53730                        expr = Expression::In(Box::new(In {
53731                            this: expr,
53732                            expressions: in_exprs,
53733                            query: None,
53734                            not: false,
53735                            global: false,
53736                            unnest: None,
53737                            is_field: false,
53738                        }));
53739                    }
53740                }
53741                // Check for alias (UNPIVOT ON (jan, feb, mar) AS q1, ...)
53742                else if self.match_token(TokenType::As) {
53743                    let alias_name = self.expect_identifier()?;
53744                    expr =
53745                        Expression::Alias(Box::new(Alias::new(expr, Identifier::new(alias_name))));
53746                }
53747
53748                on_exprs.push(expr);
53749
53750                // Continue if comma
53751                if !self.match_token(TokenType::Comma) {
53752                    break;
53753                }
53754            }
53755            on_exprs
53756        } else {
53757            Vec::new()
53758        };
53759
53760        // Parse INTO for UNPIVOT columns (INTO NAME col VALUE col, ...)
53761        let into = self.parse_unpivot_columns()?;
53762
53763        // Parse USING clause (aggregation functions with optional aliases)
53764        // e.g., USING SUM(Population), USING SUM(Population) AS total, MAX(Population) AS max
53765        // e.g., USING CAST(AVG(LENGTH(function_name)) AS INT)
53766        let using = if self.match_text_seq(&["USING"]) {
53767            let mut using_exprs = Vec::new();
53768            loop {
53769                // Stop if we hit GROUP BY or end of input
53770                if self.is_at_end() || self.check(TokenType::Group) || self.check(TokenType::RParen)
53771                {
53772                    break;
53773                }
53774                // Parse the primary expression (function call, possibly with cast :: operator)
53775                let func = self.parse_primary()?;
53776                // Check for :: cast operator (e.g., SUM(Population)::INTEGER)
53777                let expr = if self.match_token(TokenType::DColon) {
53778                    let data_type = self.parse_data_type()?;
53779                    Expression::Cast(Box::new(Cast {
53780                        this: func,
53781                        to: data_type,
53782                        trailing_comments: Vec::new(),
53783                        double_colon_syntax: true,
53784                        format: None,
53785                        default: None,
53786                        inferred_type: None,
53787                    }))
53788                } else {
53789                    func
53790                };
53791                // Try to parse alias (AS alias)
53792                if self.match_token(TokenType::As) {
53793                    let alias_name = self.expect_identifier()?;
53794                    using_exprs.push(Expression::Alias(Box::new(Alias::new(
53795                        expr,
53796                        Identifier::new(alias_name),
53797                    ))));
53798                } else {
53799                    using_exprs.push(expr);
53800                }
53801                if !self.match_token(TokenType::Comma) {
53802                    break;
53803                }
53804            }
53805            using_exprs
53806        } else {
53807            Vec::new()
53808        };
53809
53810        // Parse optional GROUP BY
53811        let group = self.parse_group()?;
53812
53813        let source = this.unwrap();
53814
53815        Ok(Some(Expression::Pivot(Box::new(Pivot {
53816            this: source,
53817            expressions,
53818            fields: Vec::new(),
53819            using,
53820            group: group.map(Box::new),
53821            unpivot: is_unpivot,
53822            into: into.map(Box::new),
53823            alias: None,
53824            include_nulls: None,
53825            default_on_null: None,
53826            with: None,
53827        }))))
53828    }
53829
53830    /// parse_slice - Parses array slice syntax [start:end:step]
53831    /// Python: _parse_slice
53832    /// Takes an optional 'this' expression (the start of the slice)
53833    pub fn parse_slice(&mut self) -> Result<Option<Expression>> {
53834        self.parse_slice_with_this(None)
53835    }
53836
53837    /// Implementation of parse_slice with 'this' parameter
53838    pub fn parse_slice_with_this(
53839        &mut self,
53840        this: Option<Expression>,
53841    ) -> Result<Option<Expression>> {
53842        // Check for colon - if not found, return this as-is
53843        if !self.match_token(TokenType::Colon) {
53844            return Ok(this);
53845        }
53846
53847        // Parse end expression
53848        // Handle special case: -: which means -1 (from end)
53849        let end = if self.check(TokenType::Dash) && self.check_next(TokenType::Colon) {
53850            // -: pattern means -1 (from end)
53851            self.skip(); // consume dash
53852            Some(Expression::Neg(Box::new(UnaryOp::new(
53853                Expression::Literal(Box::new(Literal::Number("1".to_string()))),
53854            ))))
53855        } else if self.check(TokenType::Colon) || self.check(TokenType::RBracket) {
53856            // Empty end like [start::step] or [start:]
53857            None
53858        } else {
53859            Some(self.parse_unary()?)
53860        };
53861
53862        // Parse optional step expression after second colon
53863        let step = if self.match_token(TokenType::Colon) {
53864            if self.check(TokenType::RBracket) {
53865                None
53866            } else {
53867                Some(self.parse_unary()?)
53868            }
53869        } else {
53870            None
53871        };
53872
53873        Ok(Some(Expression::Slice(Box::new(Slice {
53874            this: this.map(Box::new),
53875            expression: end.map(Box::new),
53876            step: step.map(Box::new),
53877        }))))
53878    }
53879
53880    /// Parse a slice element (start, end, or step in array slicing)
53881    /// This uses parse_unary to avoid interpreting : as parameter syntax
53882    /// Returns None for empty elements (e.g., [:] or [::step])
53883    fn parse_slice_element(&mut self) -> Result<Option<Expression>> {
53884        // Check for empty element (next is : or ])
53885        if self.check(TokenType::Colon) || self.check(TokenType::RBracket) {
53886            return Ok(None);
53887        }
53888        // Handle special case: -: means -1 (from the end)
53889        // This is used in slicing like [:-:-1] where the first -: means end=-1
53890        if self.check(TokenType::Dash) && self.check_next(TokenType::Colon) {
53891            self.skip(); // consume dash
53892                         // Don't consume the colon - let the caller handle it
53893            return Ok(Some(Expression::Neg(Box::new(UnaryOp::new(
53894                Expression::Literal(Box::new(Literal::Number("1".to_string()))),
53895            )))));
53896        }
53897        // Parse full expression (including binary ops like y - 1) but stop at : or ]
53898        let expr = self.parse_disjunction()?;
53899        Ok(expr)
53900    }
53901
53902    /// parse_sort - Ported from Python _parse_sort
53903    /// Parses SORT BY clause (Hive/Spark)
53904    #[allow(unused_variables, unused_mut)]
53905    pub fn parse_sort(&mut self) -> Result<Option<Expression>> {
53906        // Check for SORT BY token
53907        if !self.match_keywords(&[TokenType::Sort, TokenType::By]) {
53908            return Ok(None);
53909        }
53910
53911        // Parse comma-separated ordered expressions
53912        let mut expressions = Vec::new();
53913        loop {
53914            if let Some(ordered) = self.parse_ordered_item()? {
53915                expressions.push(ordered);
53916            } else {
53917                break;
53918            }
53919            if !self.match_token(TokenType::Comma) {
53920                break;
53921            }
53922        }
53923
53924        Ok(Some(Expression::SortBy(Box::new(SortBy { expressions }))))
53925    }
53926
53927    /// parse_cluster_by_clause - Parses CLUSTER BY clause (Hive/Spark)
53928    #[allow(unused_variables, unused_mut)]
53929    pub fn parse_cluster_by_clause(&mut self) -> Result<Option<Expression>> {
53930        if !self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
53931            return Ok(None);
53932        }
53933
53934        // Parse comma-separated ordered expressions
53935        let mut expressions: Vec<Ordered> = Vec::new();
53936        loop {
53937            if let Some(ordered) = self.parse_ordered_item()? {
53938                expressions.push(ordered);
53939            } else {
53940                break;
53941            }
53942            if !self.match_token(TokenType::Comma) {
53943                break;
53944            }
53945        }
53946        Ok(Some(Expression::ClusterBy(Box::new(ClusterBy {
53947            expressions,
53948        }))))
53949    }
53950
53951    /// parse_distribute_by_clause - Parses DISTRIBUTE BY clause (Hive/Spark)
53952    #[allow(unused_variables, unused_mut)]
53953    pub fn parse_distribute_by_clause(&mut self) -> Result<Option<Expression>> {
53954        if !self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
53955            return Ok(None);
53956        }
53957
53958        let expressions = self.parse_expression_list()?;
53959        Ok(Some(Expression::DistributeBy(Box::new(DistributeBy {
53960            expressions,
53961        }))))
53962    }
53963
53964    /// parse_sortkey - Redshift/PostgreSQL SORTKEY property
53965    /// Parses SORTKEY(column1, column2, ...) with optional COMPOUND modifier
53966    #[allow(unused_variables, unused_mut)]
53967    pub fn parse_sortkey(&mut self) -> Result<Option<Expression>> {
53968        // Parse the wrapped list of columns/identifiers
53969        let this = if self.match_token(TokenType::LParen) {
53970            let mut columns = Vec::new();
53971            loop {
53972                if let Some(id) = self.parse_id_var()? {
53973                    columns.push(id);
53974                } else {
53975                    break;
53976                }
53977                if !self.match_token(TokenType::Comma) {
53978                    break;
53979                }
53980            }
53981            self.match_token(TokenType::RParen);
53982
53983            if columns.is_empty() {
53984                return Ok(None);
53985            }
53986
53987            if columns.len() == 1 {
53988                columns.into_iter().next().unwrap()
53989            } else {
53990                Expression::Tuple(Box::new(Tuple {
53991                    expressions: columns,
53992                }))
53993            }
53994        } else {
53995            // Single column without parens
53996            if let Some(id) = self.parse_id_var()? {
53997                id
53998            } else {
53999                return Ok(None);
54000            }
54001        };
54002
54003        Ok(Some(Expression::SortKeyProperty(Box::new(
54004            SortKeyProperty {
54005                this: Box::new(this),
54006                compound: None, // compound is set by caller if COMPOUND keyword was matched
54007            },
54008        ))))
54009    }
54010
54011    /// parse_star - Parse STAR (*) token with optional EXCEPT/REPLACE/RENAME
54012    /// Python: if self._match(TokenType.STAR): return self._parse_star_ops()
54013    pub fn parse_star(&mut self) -> Result<Option<Expression>> {
54014        if !self.match_token(TokenType::Star) {
54015            return Ok(None);
54016        }
54017
54018        // Parse optional EXCEPT/EXCLUDE columns
54019        let except = self.parse_star_except()?;
54020
54021        // Parse optional REPLACE expressions
54022        let replace = self.parse_star_replace()?;
54023
54024        // Parse optional RENAME columns
54025        let rename = self.parse_star_rename()?;
54026
54027        Ok(Some(Expression::Star(Star {
54028            table: None,
54029            except,
54030            replace,
54031            rename,
54032            trailing_comments: Vec::new(),
54033            span: None,
54034        })))
54035    }
54036
54037    /// try_parse_identifier - Try to parse an identifier, returning None if not found
54038    fn try_parse_identifier(&mut self) -> Option<Identifier> {
54039        if self.is_identifier_token() {
54040            let token = self.advance();
54041            let quoted = token.token_type == TokenType::QuotedIdentifier;
54042            Some(Identifier {
54043                name: token.text,
54044                quoted,
54045                trailing_comments: Vec::new(),
54046                span: None,
54047            })
54048        } else {
54049            None
54050        }
54051    }
54052
54053    /// parse_star_except - Parse EXCEPT/EXCLUDE clause for Star
54054    /// Example: * EXCEPT (col1, col2)
54055    fn parse_star_except(&mut self) -> Result<Option<Vec<Identifier>>> {
54056        if !self.match_texts(&["EXCEPT", "EXCLUDE"]) {
54057            return Ok(None);
54058        }
54059
54060        // Parse (col1, col2, ...)
54061        if self.match_token(TokenType::LParen) {
54062            let mut columns = Vec::new();
54063            loop {
54064                if let Some(id) = self.try_parse_identifier() {
54065                    columns.push(id);
54066                } else if self.is_safe_keyword_as_identifier() {
54067                    // ClickHouse: allow keywords like 'key' as column names in EXCEPT
54068                    let token = self.advance();
54069                    columns.push(Identifier {
54070                        name: token.text,
54071                        quoted: false,
54072                        trailing_comments: Vec::new(),
54073                        span: None,
54074                    });
54075                } else {
54076                    break;
54077                }
54078                if !self.match_token(TokenType::Comma) {
54079                    break;
54080                }
54081            }
54082            self.match_token(TokenType::RParen);
54083            return Ok(Some(columns));
54084        }
54085
54086        // Single column without parens
54087        if let Some(id) = self.try_parse_identifier() {
54088            return Ok(Some(vec![id]));
54089        }
54090
54091        Ok(None)
54092    }
54093
54094    /// parse_star_replace - Parse REPLACE clause for Star
54095    /// Example: * REPLACE (col1 AS alias1, col2 AS alias2)
54096    fn parse_star_replace(&mut self) -> Result<Option<Vec<Alias>>> {
54097        if !self.match_texts(&["REPLACE"]) {
54098            return Ok(None);
54099        }
54100
54101        if self.match_token(TokenType::LParen) {
54102            let mut aliases = Vec::new();
54103            loop {
54104                // Parse expression AS alias
54105                if let Some(expr) = self.parse_disjunction()? {
54106                    let alias_name = if self.match_token(TokenType::As) {
54107                        self.try_parse_identifier()
54108                    } else {
54109                        None
54110                    };
54111
54112                    aliases.push(Alias {
54113                        this: expr,
54114                        alias: alias_name.unwrap_or_else(|| Identifier::new("")),
54115                        column_aliases: Vec::new(),
54116                        pre_alias_comments: Vec::new(),
54117                        trailing_comments: Vec::new(),
54118                        inferred_type: None,
54119                    });
54120                } else {
54121                    break;
54122                }
54123                if !self.match_token(TokenType::Comma) {
54124                    break;
54125                }
54126            }
54127            self.match_token(TokenType::RParen);
54128            return Ok(Some(aliases));
54129        }
54130
54131        Ok(None)
54132    }
54133
54134    /// parse_star_rename - Parse RENAME clause for Star
54135    /// Example: * RENAME (old_col AS new_col, ...)
54136    fn parse_star_rename(&mut self) -> Result<Option<Vec<(Identifier, Identifier)>>> {
54137        if !self.match_texts(&["RENAME"]) {
54138            return Ok(None);
54139        }
54140
54141        if self.match_token(TokenType::LParen) {
54142            let mut renames = Vec::new();
54143            loop {
54144                // Parse old_name AS new_name
54145                if let Some(old_name) = self.try_parse_identifier() {
54146                    if self.match_token(TokenType::As) {
54147                        if let Some(new_name) = self.try_parse_identifier() {
54148                            renames.push((old_name, new_name));
54149                        }
54150                    }
54151                } else {
54152                    break;
54153                }
54154                if !self.match_token(TokenType::Comma) {
54155                    break;
54156                }
54157            }
54158            self.match_token(TokenType::RParen);
54159            return Ok(Some(renames));
54160        }
54161
54162        Ok(None)
54163    }
54164
54165    /// parse_star_op - Helper to parse EXCEPT/REPLACE/RENAME with keywords
54166    /// Returns list of expressions if keywords match
54167    pub fn parse_star_op(&mut self, keywords: &[&str]) -> Result<Option<Vec<Expression>>> {
54168        if !self.match_texts(keywords) {
54169            return Ok(None);
54170        }
54171
54172        // If followed by paren, parse wrapped CSV
54173        if self.match_token(TokenType::LParen) {
54174            let expressions = self.parse_expression_list()?;
54175            self.match_token(TokenType::RParen);
54176            return Ok(Some(expressions));
54177        }
54178
54179        // Otherwise parse single aliased expression
54180        if let Some(expr) = self.parse_disjunction()? {
54181            // Try to parse explicit alias
54182            let result = if self.match_token(TokenType::As) {
54183                if let Some(alias_name) = self.try_parse_identifier() {
54184                    Expression::Alias(Box::new(Alias {
54185                        this: expr,
54186                        alias: alias_name,
54187                        column_aliases: Vec::new(),
54188                        pre_alias_comments: Vec::new(),
54189                        trailing_comments: Vec::new(),
54190                        inferred_type: None,
54191                    }))
54192                } else {
54193                    expr
54194                }
54195            } else {
54196                expr
54197            };
54198            return Ok(Some(vec![result]));
54199        }
54200
54201        Ok(None)
54202    }
54203
54204    /// parse_star_ops - Implemented from Python _parse_star_ops
54205    /// Creates a Star expression with EXCEPT/REPLACE/RENAME clauses
54206    /// Also handles * COLUMNS(pattern) syntax for DuckDB column selection
54207    pub fn parse_star_ops(&mut self) -> Result<Option<Expression>> {
54208        // Handle * COLUMNS(pattern) function (DuckDB)
54209        // This parses patterns like: * COLUMNS(c ILIKE '%suffix')
54210        if self.match_text_seq(&["COLUMNS"]) && self.check(TokenType::LParen) {
54211            // Parse the COLUMNS function arguments
54212            self.expect(TokenType::LParen)?;
54213            let this = self.parse_expression()?;
54214            self.expect(TokenType::RParen)?;
54215
54216            // Return a Columns expression with unpack=true (since it came from * COLUMNS())
54217            return Ok(Some(Expression::Columns(Box::new(Columns {
54218                this: Box::new(this),
54219                unpack: Some(Box::new(Expression::Boolean(BooleanLiteral {
54220                    value: true,
54221                }))),
54222            }))));
54223        }
54224
54225        // Parse EXCEPT/EXCLUDE
54226        let except_exprs = self.parse_star_op(&["EXCEPT", "EXCLUDE"])?;
54227        let except = except_exprs.map(|exprs| {
54228            exprs
54229                .into_iter()
54230                .filter_map(|e| match e {
54231                    Expression::Identifier(id) => Some(id),
54232                    Expression::Column(col) => Some(col.name),
54233                    _ => None,
54234                })
54235                .collect()
54236        });
54237
54238        // Parse REPLACE
54239        let replace_exprs = self.parse_star_op(&["REPLACE"])?;
54240        let replace = replace_exprs.map(|exprs| {
54241            exprs
54242                .into_iter()
54243                .filter_map(|e| match e {
54244                    Expression::Alias(a) => Some(*a),
54245                    _ => None,
54246                })
54247                .collect()
54248        });
54249
54250        // Parse RENAME
54251        let _rename_exprs = self.parse_star_op(&["RENAME"])?;
54252        let rename: Option<Vec<(Identifier, Identifier)>> = None; // Complex to extract from expressions
54253
54254        Ok(Some(Expression::Star(Star {
54255            table: None,
54256            except,
54257            replace,
54258            rename,
54259            trailing_comments: Vec::new(),
54260            span: None,
54261        })))
54262    }
54263
54264    /// parse_stored - Implemented from Python _parse_stored
54265    #[allow(unused_variables, unused_mut)]
54266    pub fn parse_stored(&mut self) -> Result<Option<Expression>> {
54267        if self.match_text_seq(&["BY"]) {
54268            return Ok(Some(Expression::InputOutputFormat(Box::new(
54269                InputOutputFormat {
54270                    input_format: None,
54271                    output_format: None,
54272                },
54273            ))));
54274        }
54275        if self.match_text_seq(&["INPUTFORMAT"]) {
54276            // Matched: INPUTFORMAT
54277            return Ok(None);
54278        }
54279        Ok(None)
54280    }
54281
54282    /// parse_stream - Implemented from Python _parse_stream
54283    #[allow(unused_variables, unused_mut)]
54284    pub fn parse_stream(&mut self) -> Result<Option<Expression>> {
54285        if self.match_text_seq(&["STREAM"]) {
54286            // Matched: STREAM
54287            return Ok(None);
54288        }
54289        Ok(None)
54290    }
54291
54292    /// parse_string - Parse string literal
54293    /// Python: if self._match_set(self.STRING_PARSERS): return STRING_PARSERS[token_type](...)
54294    pub fn parse_string(&mut self) -> Result<Option<Expression>> {
54295        // Regular string literal
54296        if self.match_token(TokenType::String) {
54297            let text = self.previous().text.clone();
54298            return Ok(Some(Expression::Literal(Box::new(Literal::String(text)))));
54299        }
54300        // National string (N'...')
54301        if self.match_token(TokenType::NationalString) {
54302            let text = self.previous().text.clone();
54303            return Ok(Some(Expression::Literal(Box::new(
54304                Literal::NationalString(text),
54305            ))));
54306        }
54307        // Raw string (r"..." or r'...')
54308        if self.match_token(TokenType::RawString) {
54309            let text = self.previous().text.clone();
54310            return Ok(Some(Expression::Literal(Box::new(Literal::RawString(
54311                text,
54312            )))));
54313        }
54314        // Heredoc string
54315        if self.match_token(TokenType::HeredocString) {
54316            let text = self.previous().text.clone();
54317            return Ok(Some(Expression::Literal(Box::new(Literal::String(text)))));
54318        }
54319        // Hex string (X'...' or 0x...)
54320        if self.match_token(TokenType::HexString) {
54321            let text = self.previous().text.clone();
54322            return Ok(Some(Expression::Literal(Box::new(Literal::HexString(
54323                text,
54324            )))));
54325        }
54326        // Bit string (B'...')
54327        if self.match_token(TokenType::BitString) {
54328            let text = self.previous().text.clone();
54329            return Ok(Some(Expression::Literal(Box::new(Literal::BitString(
54330                text,
54331            )))));
54332        }
54333        // Byte string (b"..." - BigQuery style)
54334        if self.match_token(TokenType::ByteString) {
54335            let text = self.previous().text.clone();
54336            return Ok(Some(Expression::Literal(Box::new(Literal::ByteString(
54337                text,
54338            )))));
54339        }
54340        Ok(None)
54341    }
54342
54343    /// parse_string_agg - Parses STRING_AGG function arguments
54344    /// Python: parser.py:6849-6899
54345    /// Handles DISTINCT, separator, ORDER BY, ON OVERFLOW, WITHIN GROUP
54346    #[allow(unused_variables, unused_mut)]
54347    pub fn parse_string_agg(&mut self) -> Result<Option<Expression>> {
54348        // Check for DISTINCT
54349        let distinct = self.match_token(TokenType::Distinct);
54350
54351        // Parse main expression
54352        let this = self.parse_disjunction()?;
54353        if this.is_none() {
54354            return Ok(None);
54355        }
54356
54357        // Parse optional separator
54358        let separator = if self.match_token(TokenType::Comma) {
54359            self.parse_disjunction()?
54360        } else {
54361            None
54362        };
54363
54364        // Parse ON OVERFLOW clause
54365        let on_overflow = if self.match_text_seq(&["ON", "OVERFLOW"]) {
54366            if self.match_text_seq(&["ERROR"]) {
54367                Some(Box::new(Expression::Var(Box::new(Var {
54368                    this: "ERROR".to_string(),
54369                }))))
54370            } else {
54371                self.match_text_seq(&["TRUNCATE"]);
54372                let truncate_str = self.parse_string()?;
54373                let with_count = if self.match_text_seq(&["WITH", "COUNT"]) {
54374                    Some(true)
54375                } else if self.match_text_seq(&["WITHOUT", "COUNT"]) {
54376                    Some(false)
54377                } else {
54378                    None
54379                };
54380                Some(Box::new(Expression::OverflowTruncateBehavior(Box::new(
54381                    OverflowTruncateBehavior {
54382                        this: truncate_str.map(Box::new),
54383                        with_count: with_count
54384                            .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
54385                    },
54386                ))))
54387            }
54388        } else {
54389            None
54390        };
54391
54392        // Parse ORDER BY or WITHIN GROUP
54393        let order_by = if self.match_token(TokenType::OrderBy) {
54394            Some(self.parse_expression_list()?)
54395        } else if self.match_text_seq(&["WITHIN", "GROUP"]) {
54396            self.match_token(TokenType::LParen);
54397            let order = self.parse_order()?;
54398            self.match_token(TokenType::RParen);
54399            order.map(|o| vec![o])
54400        } else {
54401            None
54402        };
54403
54404        // Return as GroupConcat (which is the canonical form for STRING_AGG)
54405        Ok(Some(Expression::GroupConcat(Box::new(GroupConcatFunc {
54406            this: this.unwrap(),
54407            separator: separator,
54408            order_by: None,
54409            distinct,
54410            filter: None,
54411            inferred_type: None,
54412        }))))
54413    }
54414
54415    /// parse_string_as_identifier - Parses a string literal as a quoted identifier
54416    /// Python: _parse_string_as_identifier
54417    /// Used for cases where a string can be used as an identifier (e.g., MySQL)
54418    pub fn parse_string_as_identifier(&mut self) -> Result<Option<Expression>> {
54419        if self.match_token(TokenType::String) {
54420            let text = self.previous().text.clone();
54421            // Remove quotes if present
54422            let name = if text.starts_with('\'') && text.ends_with('\'') && text.len() >= 2 {
54423                text[1..text.len() - 1].to_string()
54424            } else if text.starts_with('"') && text.ends_with('"') && text.len() >= 2 {
54425                text[1..text.len() - 1].to_string()
54426            } else {
54427                text
54428            };
54429
54430            Ok(Some(Expression::Identifier(Identifier {
54431                name,
54432                quoted: true,
54433                trailing_comments: Vec::new(),
54434                span: None,
54435            })))
54436        } else {
54437            Ok(None)
54438        }
54439    }
54440
54441    /// parse_struct_types - Delegates to parse_types
54442    #[allow(unused_variables, unused_mut)]
54443    pub fn parse_struct_types(&mut self) -> Result<Option<Expression>> {
54444        self.parse_types()
54445    }
54446
54447    /// parse_subquery - Ported from Python _parse_subquery
54448    /// Parses a parenthesized SELECT as subquery: (SELECT ...)
54449    #[allow(unused_variables, unused_mut)]
54450    pub fn parse_subquery(&mut self) -> Result<Option<Expression>> {
54451        // Check for opening paren
54452        if !self.match_token(TokenType::LParen) {
54453            return Ok(None);
54454        }
54455
54456        // Check if it's a SELECT or WITH statement
54457        if !self.check(TokenType::Select) && !self.check(TokenType::With) {
54458            // Not a subquery, retreat
54459            self.current -= 1;
54460            return Ok(None);
54461        }
54462
54463        // Parse the query
54464        let query = self.parse_statement()?;
54465        self.expect(TokenType::RParen)?;
54466
54467        // Parse optional table alias
54468        let alias = self.parse_table_alias_if_present()?;
54469
54470        Ok(Some(Expression::Subquery(Box::new(Subquery {
54471            this: query,
54472            alias,
54473            column_aliases: Vec::new(),
54474            order_by: None,
54475            limit: None,
54476            offset: None,
54477            lateral: false,
54478            modifiers_inside: false,
54479            trailing_comments: Vec::new(),
54480            distribute_by: None,
54481            sort_by: None,
54482            cluster_by: None,
54483            inferred_type: None,
54484        }))))
54485    }
54486
54487    /// Helper to parse table alias if present
54488    fn parse_table_alias_if_present(&mut self) -> Result<Option<Identifier>> {
54489        // Check for AS keyword
54490        let explicit_as = self.match_token(TokenType::As);
54491
54492        // ClickHouse: keywords can be used as table aliases when AS is explicit
54493        let is_keyword_alias = explicit_as
54494            && matches!(
54495                self.config.dialect,
54496                Some(crate::dialects::DialectType::ClickHouse)
54497            )
54498            && self.peek().token_type.is_keyword();
54499
54500        // Try to parse identifier
54501        if self.check(TokenType::Identifier)
54502            || self.check(TokenType::QuotedIdentifier)
54503            || is_keyword_alias
54504        {
54505            if is_keyword_alias
54506                && !self.check(TokenType::Identifier)
54507                && !self.check(TokenType::QuotedIdentifier)
54508            {
54509                let token = self.advance();
54510                return Ok(Some(Identifier::new(token.text)));
54511            }
54512            if let Some(Expression::Identifier(id)) = self.parse_identifier()? {
54513                return Ok(Some(id));
54514            }
54515        } else if explicit_as {
54516            // AS was present but no identifier follows - this is an error
54517            return Err(self.parse_error("Expected identifier after AS"));
54518        }
54519
54520        Ok(None)
54521    }
54522
54523    /// parse_substring - Ported from Python _parse_substring
54524    /// Parses SUBSTRING function with two syntax variants:
54525    /// 1. Standard SQL: SUBSTRING(str FROM start [FOR length])
54526    /// 2. Function style: SUBSTRING(str, start, length)
54527    #[allow(unused_variables, unused_mut)]
54528    pub fn parse_substring(&mut self) -> Result<Option<Expression>> {
54529        // Parse initial comma-separated arguments
54530        let mut args: Vec<Expression> = Vec::new();
54531
54532        // Parse first argument (the string)
54533        match self.parse_bitwise() {
54534            Ok(Some(expr)) => {
54535                let expr = self.try_clickhouse_func_arg_alias(expr);
54536                args.push(expr);
54537            }
54538            Ok(None) => return Ok(None),
54539            Err(e) => return Err(e),
54540        }
54541
54542        // Check for comma-separated additional arguments
54543        while self.match_token(TokenType::Comma) {
54544            match self.parse_bitwise() {
54545                Ok(Some(expr)) => {
54546                    let expr = self.try_clickhouse_func_arg_alias(expr);
54547                    args.push(expr);
54548                }
54549                Ok(None) => break,
54550                Err(e) => return Err(e),
54551            }
54552        }
54553
54554        // Check for FROM/FOR syntax (SQL standard)
54555        let mut start: Option<Expression> = None;
54556        let mut length: Option<Expression> = None;
54557        let mut from_for_syntax = false;
54558
54559        loop {
54560            if self.match_token(TokenType::From) {
54561                from_for_syntax = true;
54562                match self.parse_bitwise() {
54563                    Ok(Some(expr)) => {
54564                        let expr = self.try_clickhouse_func_arg_alias(expr);
54565                        start = Some(expr);
54566                    }
54567                    Ok(None) => {}
54568                    Err(e) => return Err(e),
54569                }
54570            } else if self.match_token(TokenType::For) {
54571                from_for_syntax = true;
54572                // If no start specified yet, default to 1
54573                if start.is_none() {
54574                    start = Some(Expression::Literal(Box::new(Literal::Number(
54575                        "1".to_string(),
54576                    ))));
54577                }
54578                match self.parse_bitwise() {
54579                    Ok(Some(expr)) => {
54580                        let expr = self.try_clickhouse_func_arg_alias(expr);
54581                        length = Some(expr);
54582                    }
54583                    Ok(None) => {}
54584                    Err(e) => return Err(e),
54585                }
54586            } else {
54587                break;
54588            }
54589        }
54590
54591        // Build the substring expression
54592        if args.is_empty() {
54593            return Ok(None);
54594        }
54595
54596        let this = args.remove(0);
54597
54598        // Determine start and length
54599        let final_start = if let Some(s) = start {
54600            s
54601        } else if !args.is_empty() {
54602            args.remove(0)
54603        } else {
54604            Expression::Literal(Box::new(Literal::Number("1".to_string())))
54605        };
54606
54607        let final_length = if length.is_some() {
54608            length
54609        } else if !args.is_empty() {
54610            Some(args.remove(0))
54611        } else {
54612            None
54613        };
54614
54615        Ok(Some(Expression::Substring(Box::new(SubstringFunc {
54616            this,
54617            start: final_start,
54618            length: final_length,
54619            from_for_syntax,
54620        }))))
54621    }
54622
54623    /// parse_system_versioning_property - Implemented from Python _parse_system_versioning_property
54624    /// Calls: parse_table_parts, parse_retention_period
54625    #[allow(unused_variables, unused_mut)]
54626    pub fn parse_system_versioning_property(&mut self) -> Result<Option<Expression>> {
54627        if self.match_text_seq(&["OFF"]) {
54628            return Ok(Some(Expression::WithSystemVersioningProperty(Box::new(
54629                WithSystemVersioningProperty {
54630                    on: None,
54631                    this: None,
54632                    data_consistency: None,
54633                    retention_period: None,
54634                    with_: None,
54635                },
54636            ))));
54637        }
54638        if self.match_text_seq(&["HISTORY_TABLE", "="]) {
54639            // Matched: HISTORY_TABLE =
54640            return Ok(None);
54641        }
54642        if self.match_text_seq(&["DATA_CONSISTENCY_CHECK", "="]) {
54643            // Matched: DATA_CONSISTENCY_CHECK =
54644            return Ok(None);
54645        }
54646        Ok(None)
54647    }
54648
54649    /// Parse PostgreSQL ROWS FROM syntax:
54650    /// ROWS FROM (func1(args) AS alias1(col1 type1, col2 type2), func2(...) AS alias2(...)) [WITH ORDINALITY] [AS outer_alias(...)]
54651    fn parse_rows_from(&mut self) -> Result<Expression> {
54652        // Expect opening paren
54653        self.expect(TokenType::LParen)?;
54654
54655        let mut expressions = Vec::new();
54656
54657        loop {
54658            // Parse each function expression inside ROWS FROM
54659            // Each element is: func_name(args) [AS alias(col1 type1, col2 type2, ...)]
54660            let func_expr = self.parse_rows_from_function()?;
54661            expressions.push(func_expr);
54662
54663            if !self.match_token(TokenType::Comma) {
54664                break;
54665            }
54666        }
54667
54668        self.expect(TokenType::RParen)?;
54669
54670        // Check for WITH ORDINALITY
54671        let ordinality =
54672            if self.match_token(TokenType::With) && self.match_token(TokenType::Ordinality) {
54673                true
54674            } else {
54675                false
54676            };
54677
54678        // Check for outer alias: AS alias(col1 type1, col2 type2, ...)
54679        let alias = if self.match_token(TokenType::As) {
54680            Some(Box::new(self.parse_rows_from_alias()?))
54681        } else {
54682            None
54683        };
54684
54685        Ok(Expression::RowsFrom(Box::new(RowsFrom {
54686            expressions,
54687            ordinality,
54688            alias,
54689        })))
54690    }
54691
54692    /// Parse a single function in ROWS FROM: func_name(args) [AS alias(col1 type1, ...)]
54693    fn parse_rows_from_function(&mut self) -> Result<Expression> {
54694        // Parse function name
54695        let func_name = self.expect_identifier_or_keyword()?;
54696
54697        // Parse function arguments
54698        self.expect(TokenType::LParen)?;
54699        let args = if self.check(TokenType::RParen) {
54700            Vec::new()
54701        } else {
54702            self.parse_function_arguments()?
54703        };
54704        self.expect(TokenType::RParen)?;
54705
54706        let func_expr = Expression::Function(Box::new(Function {
54707            name: func_name,
54708            args,
54709            distinct: false,
54710            trailing_comments: Vec::new(),
54711            use_bracket_syntax: false,
54712            no_parens: false,
54713            quoted: false,
54714            span: None,
54715            inferred_type: None,
54716        }));
54717
54718        // Check for AS alias(col1 type1, col2 type2, ...)
54719        // Return a Tuple(function, TableAlias) so the generator can output: FUNC() AS alias(col type)
54720        if self.match_token(TokenType::As) {
54721            let alias_expr = self.parse_rows_from_alias()?;
54722            Ok(Expression::Tuple(Box::new(Tuple {
54723                expressions: vec![func_expr, alias_expr],
54724            })))
54725        } else {
54726            Ok(func_expr)
54727        }
54728    }
54729
54730    /// Parse ROWS FROM alias with typed columns: alias_name(col1 type1, col2 type2, ...)
54731    fn parse_rows_from_alias(&mut self) -> Result<Expression> {
54732        let alias_name = self.expect_identifier_or_keyword_with_quoted()?;
54733
54734        // Check for column definitions: (col1 type1, col2 type2, ...)
54735        let columns = if self.match_token(TokenType::LParen) {
54736            let mut cols = Vec::new();
54737            loop {
54738                if self.check(TokenType::RParen) {
54739                    break;
54740                }
54741                // Parse column name (can be quoted)
54742                let col_name = self.expect_identifier_or_keyword_with_quoted()?;
54743                // Parse column type
54744                let col_type = self.parse_data_type()?;
54745                // Create ColumnDef expression, preserving the quoted status
54746                let mut col_def = ColumnDef::new(col_name.name.clone(), col_type);
54747                col_def.name = col_name; // Preserve the full identifier with quoted flag
54748                cols.push(Expression::ColumnDef(Box::new(col_def)));
54749
54750                if !self.match_token(TokenType::Comma) {
54751                    break;
54752                }
54753            }
54754            self.expect(TokenType::RParen)?;
54755            cols
54756        } else {
54757            Vec::new()
54758        };
54759
54760        Ok(Expression::TableAlias(Box::new(TableAlias {
54761            this: Some(Box::new(Expression::Identifier(alias_name))),
54762            columns,
54763        })))
54764    }
54765
54766    /// parse_table - Implemented from Python _parse_table
54767    /// Calls: parse_table_hints, parse_unnest, parse_partition
54768    #[allow(unused_variables, unused_mut)]
54769    pub fn parse_table(&mut self) -> Result<Option<Expression>> {
54770        if self.match_text_seq(&["ROWS", "FROM"]) {
54771            // ROWS FROM is handled by parse_rows_from() in parse_table_expression()
54772            return Ok(None);
54773        }
54774        if self.match_text_seq(&["*"]) {
54775            // Matched: *
54776            return Ok(None);
54777        }
54778        if self.match_text_seq(&["NOT", "INDEXED"]) {
54779            // Matched: NOT INDEXED
54780            return Ok(None);
54781        }
54782        Ok(None)
54783    }
54784
54785    /// parse_table_alias - Ported from Python _parse_table_alias
54786    /// Parses table alias: AS alias [(col1, col2, ...)]
54787    #[allow(unused_variables, unused_mut)]
54788    pub fn parse_table_alias(&mut self) -> Result<Option<Expression>> {
54789        // Check for AS keyword (optional in most dialects)
54790        let has_as = self.match_token(TokenType::As);
54791
54792        // Handle AS (col1, col2) - no alias name, just column aliases
54793        if has_as && self.check(TokenType::LParen) {
54794            // Parse (col1, col2, ...)
54795            self.skip(); // consume LParen
54796            let mut cols = Vec::new();
54797            loop {
54798                if self.check(TokenType::RParen) {
54799                    break;
54800                }
54801                if let Ok(Some(col)) = self.parse_id_var() {
54802                    cols.push(col);
54803                }
54804                if !self.match_token(TokenType::Comma) {
54805                    break;
54806                }
54807            }
54808            self.expect(TokenType::RParen)?;
54809            return Ok(Some(Expression::TableAlias(Box::new(TableAlias {
54810                this: None,
54811                columns: cols,
54812            }))));
54813        }
54814
54815        // Parse the alias identifier
54816        // ClickHouse: keywords can be used as table aliases (e.g., AS select, AS from)
54817        let is_keyword_alias = has_as
54818            && matches!(
54819                self.config.dialect,
54820                Some(crate::dialects::DialectType::ClickHouse)
54821            )
54822            && self.peek().token_type.is_keyword();
54823        if !self.check(TokenType::Identifier)
54824            && !self.check(TokenType::QuotedIdentifier)
54825            && !self.check(TokenType::Var)
54826            && !is_keyword_alias
54827        {
54828            if has_as {
54829                return Err(self.parse_error("Expected identifier after AS"));
54830            }
54831            return Ok(None);
54832        }
54833
54834        let alias_token = self.advance();
54835        let is_quoted = alias_token.token_type == TokenType::QuotedIdentifier;
54836        let mut alias_ident = Identifier::new(alias_token.text.clone());
54837        if is_quoted {
54838            alias_ident.quoted = true;
54839        }
54840        let alias = Expression::Identifier(alias_ident);
54841
54842        // Check for column list: (col1, col2, ...)
54843        let columns = if self.match_token(TokenType::LParen) {
54844            let mut cols = Vec::new();
54845            loop {
54846                if self.check(TokenType::RParen) {
54847                    break;
54848                }
54849                if let Ok(Some(col)) = self.parse_id_var() {
54850                    cols.push(col);
54851                }
54852                if !self.match_token(TokenType::Comma) {
54853                    break;
54854                }
54855            }
54856            self.expect(TokenType::RParen)?;
54857            cols
54858        } else {
54859            Vec::new()
54860        };
54861
54862        Ok(Some(Expression::TableAlias(Box::new(TableAlias {
54863            this: Some(Box::new(alias)),
54864            columns,
54865        }))))
54866    }
54867
54868    /// parse_table_hints - Ported from Python _parse_table_hints
54869    /// Parses table hints (SQL Server WITH (...) or MySQL USE/IGNORE/FORCE INDEX)
54870    #[allow(unused_variables, unused_mut)]
54871    pub fn parse_table_hints(&mut self) -> Result<Option<Expression>> {
54872        let mut hints = Vec::new();
54873
54874        // SQL Server style: WITH (hint1, hint2, ...)
54875        if self.match_text_seq(&["WITH"]) && self.match_token(TokenType::LParen) {
54876            let mut expressions = Vec::new();
54877            loop {
54878                // Parse function or variable as hint
54879                if let Some(func) = self.parse_function()? {
54880                    expressions.push(func);
54881                } else if let Some(var) = self.parse_var()? {
54882                    expressions.push(var);
54883                } else {
54884                    break;
54885                }
54886                if !self.match_token(TokenType::Comma) {
54887                    break;
54888                }
54889            }
54890            self.match_token(TokenType::RParen);
54891
54892            if !expressions.is_empty() {
54893                hints.push(Expression::WithTableHint(Box::new(WithTableHint {
54894                    expressions,
54895                })));
54896            }
54897        } else {
54898            // MySQL style: USE INDEX, IGNORE INDEX, FORCE INDEX
54899            while self.match_texts(&["USE", "IGNORE", "FORCE"]) {
54900                let hint_type = self.previous().text.to_ascii_uppercase();
54901
54902                // Match INDEX or KEY
54903                let _ = self.match_texts(&["INDEX", "KEY"]);
54904
54905                // Check for optional FOR clause: FOR JOIN, FOR ORDER BY, FOR GROUP BY
54906                let target = if self.match_text_seq(&["FOR"]) {
54907                    let target_token = self.advance();
54908                    let target_text = target_token.text.to_ascii_uppercase();
54909                    // For ORDER BY and GROUP BY, combine into a single target name
54910                    let full_target = if (target_text == "ORDER" || target_text == "GROUP")
54911                        && self.check(TokenType::By)
54912                    {
54913                        self.skip(); // consume BY
54914                        format!("{} BY", target_text)
54915                    } else {
54916                        target_text
54917                    };
54918                    Some(Box::new(Expression::Identifier(Identifier {
54919                        name: full_target,
54920                        quoted: false,
54921                        trailing_comments: Vec::new(),
54922                        span: None,
54923                    })))
54924                } else {
54925                    None
54926                };
54927
54928                // Parse wrapped identifiers (index names — can include keywords like PRIMARY)
54929                let expressions = if self.match_token(TokenType::LParen) {
54930                    let mut ids = Vec::new();
54931                    loop {
54932                        if self.check(TokenType::RParen) {
54933                            break;
54934                        }
54935                        if let Some(id) = self.parse_id_var()? {
54936                            ids.push(id);
54937                        } else if self.is_safe_keyword_as_identifier()
54938                            || self.check(TokenType::PrimaryKey)
54939                        {
54940                            // Accept keywords as index names (e.g., PRIMARY)
54941                            let name = self.advance().text.clone();
54942                            ids.push(Expression::Identifier(Identifier::new(name)));
54943                        } else {
54944                            break;
54945                        }
54946                        if !self.match_token(TokenType::Comma) {
54947                            break;
54948                        }
54949                    }
54950                    self.match_token(TokenType::RParen);
54951                    ids
54952                } else {
54953                    Vec::new()
54954                };
54955
54956                hints.push(Expression::IndexTableHint(Box::new(IndexTableHint {
54957                    this: Box::new(Expression::Identifier(Identifier {
54958                        name: hint_type,
54959                        quoted: false,
54960                        trailing_comments: Vec::new(),
54961                        span: None,
54962                    })),
54963                    expressions,
54964                    target,
54965                })));
54966            }
54967        }
54968
54969        if hints.is_empty() {
54970            return Ok(None);
54971        }
54972
54973        // Return as a Tuple containing hints
54974        Ok(Some(Expression::Tuple(Box::new(Tuple {
54975            expressions: hints,
54976        }))))
54977    }
54978
54979    /// Parse TSQL TRUNCATE table hints: WITH (PARTITIONS(1, 2 TO 5, 10 TO 20, 84))
54980    /// Unlike regular table hints, PARTITIONS arguments can contain TO ranges.
54981    pub fn parse_truncate_table_hints(&mut self) -> Result<Option<Expression>> {
54982        if !self.match_text_seq(&["WITH"]) || !self.match_token(TokenType::LParen) {
54983            return Ok(None);
54984        }
54985
54986        let mut hints = Vec::new();
54987
54988        // Check for PARTITIONS specifically
54989        if self.check_identifier("PARTITIONS") {
54990            self.skip(); // consume PARTITIONS
54991            self.expect(TokenType::LParen)?;
54992
54993            // Parse partition ranges: 1, 2 TO 5, 10 TO 20, 84
54994            let mut parts = Vec::new();
54995            loop {
54996                if self.check(TokenType::RParen) {
54997                    break;
54998                }
54999                let low = self.parse_primary()?;
55000                if self.match_text_seq(&["TO"]) {
55001                    let high = self.parse_primary()?;
55002                    parts.push(Expression::PartitionRange(Box::new(PartitionRange {
55003                        this: Box::new(low),
55004                        expression: Some(Box::new(high)),
55005                        expressions: Vec::new(),
55006                    })));
55007                } else {
55008                    parts.push(low);
55009                }
55010                if !self.match_token(TokenType::Comma) {
55011                    break;
55012                }
55013            }
55014            self.expect(TokenType::RParen)?; // close PARTITIONS(...)
55015
55016            // Create an Anonymous function for PARTITIONS(...)
55017            hints.push(Expression::Anonymous(Box::new(Anonymous {
55018                this: Box::new(Expression::Identifier(Identifier {
55019                    name: "PARTITIONS".to_string(),
55020                    quoted: false,
55021                    trailing_comments: Vec::new(),
55022                    span: None,
55023                })),
55024                expressions: parts,
55025            })));
55026        } else {
55027            // Fall back to regular hint parsing (function or var)
55028            loop {
55029                if let Some(func) = self.parse_function()? {
55030                    hints.push(func);
55031                } else if let Some(var) = self.parse_var()? {
55032                    hints.push(var);
55033                } else {
55034                    break;
55035                }
55036                if !self.match_token(TokenType::Comma) {
55037                    break;
55038                }
55039            }
55040        }
55041
55042        self.expect(TokenType::RParen)?; // close WITH(...)
55043
55044        if hints.is_empty() {
55045            return Ok(None);
55046        }
55047
55048        // Wrap in WithTableHint then Tuple (same as parse_table_hints)
55049        let hint = Expression::WithTableHint(Box::new(WithTableHint { expressions: hints }));
55050
55051        Ok(Some(Expression::Tuple(Box::new(Tuple {
55052            expressions: vec![hint],
55053        }))))
55054    }
55055
55056    /// parse_table_part - Parse a single part of a table reference
55057    /// Tries: identifier, string as identifier, placeholder
55058    #[allow(unused_variables, unused_mut)]
55059    pub fn parse_table_part(&mut self) -> Result<Option<Expression>> {
55060        // Try to parse an identifier
55061        if let Some(id) = self.parse_id_var()? {
55062            return Ok(Some(id));
55063        }
55064
55065        // Try to parse a string as identifier
55066        if let Some(str_id) = self.parse_string_as_identifier()? {
55067            return Ok(Some(str_id));
55068        }
55069
55070        // Try to parse a placeholder
55071        if let Some(placeholder) = self.parse_placeholder()? {
55072            return Ok(Some(placeholder));
55073        }
55074
55075        // Accept keywords as identifiers in table part context (e.g., db.cluster where "cluster" is a keyword)
55076        // This mirrors Python sqlglot's ID_VAR_TOKENS which includes many keyword types
55077        if self.check_keyword_as_identifier() {
55078            let text = self.peek().text.clone();
55079            self.skip();
55080            return Ok(Some(Expression::Identifier(Identifier {
55081                name: text,
55082                quoted: false,
55083                trailing_comments: Vec::new(),
55084                span: None,
55085            })));
55086        }
55087
55088        Ok(None)
55089    }
55090
55091    /// Check if the current token is a keyword that can be used as an identifier in certain contexts
55092    /// This includes many SQL keywords like CLUSTER, TABLE, INDEX, etc.
55093    fn check_keyword_as_identifier(&self) -> bool {
55094        if self.is_at_end() {
55095            return false;
55096        }
55097        let token_type = self.peek().token_type;
55098        // Keywords that can be used as identifiers (similar to Python's ID_VAR_TOKENS)
55099        matches!(
55100            token_type,
55101            TokenType::Cluster
55102                | TokenType::Table
55103                | TokenType::Index
55104                | TokenType::View
55105                | TokenType::Database
55106                | TokenType::Schema
55107                | TokenType::Column
55108                | TokenType::Function
55109                | TokenType::Procedure
55110                | TokenType::Constraint
55111                | TokenType::Sequence
55112                | TokenType::Type
55113                | TokenType::Partition
55114                | TokenType::Comment
55115                | TokenType::Cache
55116                | TokenType::Commit
55117                | TokenType::Begin
55118                | TokenType::End
55119                | TokenType::Set
55120                | TokenType::Show
55121                | TokenType::Describe
55122                | TokenType::Use
55123                | TokenType::Execute
55124                | TokenType::Delete
55125                | TokenType::Update
55126                | TokenType::Merge
55127                | TokenType::Load
55128                | TokenType::Copy
55129                | TokenType::Truncate
55130                | TokenType::Replace
55131                | TokenType::Refresh
55132                | TokenType::Rename
55133                | TokenType::Filter
55134                | TokenType::Format
55135                | TokenType::First
55136                | TokenType::Next
55137                | TokenType::Last
55138                | TokenType::Keep
55139                | TokenType::Match
55140                | TokenType::Over
55141                | TokenType::Range
55142                | TokenType::Rows
55143                | TokenType::Row
55144                | TokenType::Offset
55145                | TokenType::Limit
55146                | TokenType::Top
55147                | TokenType::Cube
55148                | TokenType::Rollup
55149                | TokenType::Pivot
55150                | TokenType::Unpivot
55151                | TokenType::Window
55152                | TokenType::Recursive
55153                | TokenType::Unique
55154                | TokenType::Temporary
55155                | TokenType::Volatile
55156                | TokenType::References
55157                | TokenType::Natural
55158                | TokenType::Left
55159                | TokenType::Right
55160                | TokenType::Full
55161                | TokenType::Semi
55162                | TokenType::Anti
55163                | TokenType::Apply
55164                | TokenType::All
55165                | TokenType::Asc
55166                | TokenType::Desc
55167                | TokenType::Analyze
55168        )
55169    }
55170
55171    /// parse_table_parts - Parse catalog.schema.table or schema.table or table
55172    /// Returns a Table expression with all parts
55173    #[allow(unused_variables, unused_mut)]
55174    pub fn parse_table_parts(&mut self) -> Result<Option<Expression>> {
55175        // Parse the first part
55176        let first = self.parse_table_part()?;
55177        if first.is_none() {
55178            return Ok(None);
55179        }
55180
55181        let mut parts = vec![first.unwrap()];
55182
55183        // Parse additional dot-separated parts
55184        while self.match_token(TokenType::Dot) {
55185            if let Some(part) = self.parse_table_part()? {
55186                parts.push(part);
55187            } else {
55188                break;
55189            }
55190        }
55191
55192        // Convert parts to Table expression
55193        // Last part is table name, second-to-last is schema, third-to-last is catalog
55194        let (catalog, schema, name) = match parts.len() {
55195            1 => (None, None, parts.pop().unwrap()),
55196            2 => {
55197                let table = parts.pop().unwrap();
55198                let schema = parts.pop().unwrap();
55199                (None, Some(schema), table)
55200            }
55201            _ => {
55202                let table = parts.pop().unwrap();
55203                let schema = parts.pop().unwrap();
55204                let catalog = parts.pop();
55205                (catalog, Some(schema), table)
55206            }
55207        };
55208
55209        // Extract identifier from Expression
55210        let name_ident = match name {
55211            Expression::Identifier(id) => id,
55212            _ => Identifier::new(String::new()),
55213        };
55214        let schema_ident = schema.map(|s| match s {
55215            Expression::Identifier(id) => id,
55216            _ => Identifier::new(String::new()),
55217        });
55218        let catalog_ident = catalog.map(|c| match c {
55219            Expression::Identifier(id) => id,
55220            _ => Identifier::new(String::new()),
55221        });
55222
55223        Ok(Some(Expression::boxed_table(TableRef {
55224            name: name_ident,
55225            schema: schema_ident,
55226            catalog: catalog_ident,
55227            alias: None,
55228            alias_explicit_as: false,
55229            column_aliases: Vec::new(),
55230            leading_comments: Vec::new(),
55231            trailing_comments: Vec::new(),
55232            when: None,
55233            only: false,
55234            final_: false,
55235            table_sample: None,
55236            hints: Vec::new(),
55237            system_time: None,
55238            partitions: Vec::new(),
55239            identifier_func: None,
55240            changes: None,
55241            version: None,
55242            span: None,
55243        })))
55244    }
55245
55246    /// parse_table_sample - Implemented from Python _parse_table_sample
55247    /// Calls: parse_number, parse_factor, parse_placeholder
55248    #[allow(unused_variables, unused_mut)]
55249    pub fn parse_table_sample(&mut self) -> Result<Option<Expression>> {
55250        if self.match_text_seq(&["USING", "SAMPLE"]) {
55251            return Ok(Some(Expression::TableSample(Box::new(TableSample {
55252                this: None,
55253                sample: None,
55254                expressions: Vec::new(),
55255                method: None,
55256                bucket_numerator: None,
55257                bucket_denominator: None,
55258                bucket_field: None,
55259                percent: None,
55260                rows: None,
55261                size: None,
55262                seed: None,
55263            }))));
55264        }
55265        if self.match_text_seq(&["BUCKET"]) {
55266            // Matched: BUCKET
55267            return Ok(None);
55268        }
55269        if self.match_text_seq(&["OUT", "OF"]) {
55270            // Matched: OUT OF
55271            return Ok(None);
55272        }
55273        if self.match_texts(&["SEED", "REPEATABLE"]) {
55274            // Matched one of: SEED, REPEATABLE
55275            return Ok(None);
55276        }
55277        Ok(None)
55278    }
55279
55280    /// parse_term - Parses addition/subtraction expressions (+ - operators)
55281    /// Python: _parse_term
55282    /// Delegates to the existing parse_addition in the operator precedence chain
55283    pub fn parse_term(&mut self) -> Result<Option<Expression>> {
55284        // Delegate to the existing addition parsing
55285        match self.parse_addition() {
55286            Ok(expr) => Ok(Some(expr)),
55287            Err(_) => Ok(None),
55288        }
55289    }
55290
55291    /// parse_to_table - ClickHouse TO table property
55292    /// Parses: TO table_name
55293    #[allow(unused_variables, unused_mut)]
55294    pub fn parse_to_table(&mut self) -> Result<Option<Expression>> {
55295        // Parse the table reference
55296        let table = self.parse_table_parts()?;
55297        if table.is_none() {
55298            return Ok(None);
55299        }
55300
55301        Ok(Some(Expression::ToTableProperty(Box::new(
55302            ToTableProperty {
55303                this: Box::new(table.unwrap()),
55304            },
55305        ))))
55306    }
55307
55308    /// parse_tokens - Operator precedence parser
55309    #[allow(unused_variables, unused_mut)]
55310    pub fn parse_tokens(&mut self) -> Result<Option<Expression>> {
55311        // Uses operator precedence parsing pattern
55312        Ok(None)
55313    }
55314
55315    /// parse_trim - Ported from Python _parse_trim
55316    /// Parses TRIM function: TRIM([BOTH|LEADING|TRAILING] chars FROM str) or TRIM(str, chars)
55317    #[allow(unused_variables, unused_mut)]
55318    pub fn parse_trim(&mut self) -> Result<Option<Expression>> {
55319        // Check for position keyword (BOTH, LEADING, TRAILING)
55320        let (position, position_explicit) = if self.match_texts(&["BOTH"]) {
55321            (TrimPosition::Both, true)
55322        } else if self.match_texts(&["LEADING"]) {
55323            (TrimPosition::Leading, true)
55324        } else if self.match_texts(&["TRAILING"]) {
55325            (TrimPosition::Trailing, true)
55326        } else {
55327            (TrimPosition::Both, false)
55328        };
55329
55330        // Parse first expression
55331        let first = match self.parse_bitwise() {
55332            Ok(Some(expr)) => self.try_clickhouse_func_arg_alias(expr),
55333            Ok(None) => return Ok(None),
55334            Err(e) => return Err(e),
55335        };
55336
55337        // Check for FROM or comma to see if there's a second expression
55338        let (this, characters, sql_standard_syntax) = if self.match_token(TokenType::From) {
55339            // SQL standard syntax: TRIM([position] chars FROM str)
55340            let second = match self.parse_bitwise() {
55341                Ok(Some(expr)) => self.try_clickhouse_func_arg_alias(expr),
55342                Ok(None) => return Err(self.parse_error("Expected expression after FROM in TRIM")),
55343                Err(e) => return Err(e),
55344            };
55345            // In SQL standard syntax: first is characters, second is the string
55346            (second, Some(first), true)
55347        } else if self.match_token(TokenType::Comma) {
55348            // Function syntax: TRIM(a, b)
55349            let second = match self.parse_bitwise() {
55350                Ok(Some(expr)) => Some(expr),
55351                Ok(None) => None,
55352                Err(e) => return Err(e),
55353            };
55354            // In Spark, comma syntax is TRIM(chars, str) - pattern first
55355            // In other dialects, comma syntax is TRIM(str, chars) - string first
55356            let trim_pattern_first = matches!(
55357                self.config.dialect,
55358                Some(crate::dialects::DialectType::Spark)
55359            );
55360            if trim_pattern_first && second.is_some() {
55361                // first=chars, second=str
55362                (second.unwrap(), Some(first), false)
55363            } else {
55364                (first, second, false)
55365            }
55366        } else {
55367            // Single argument: TRIM(str)
55368            (first, None, false)
55369        };
55370
55371        Ok(Some(Expression::Trim(Box::new(TrimFunc {
55372            this,
55373            characters,
55374            position,
55375            sql_standard_syntax,
55376            position_explicit,
55377        }))))
55378    }
55379
55380    /// parse_truncate_table - Implemented from Python _parse_truncate_table
55381    /// Calls: parse_on_property, parse_partition, parse_function
55382    #[allow(unused_variables, unused_mut)]
55383    pub fn parse_truncate_table(&mut self) -> Result<Option<Expression>> {
55384        if self.match_text_seq(&["RESTART", "IDENTITY"]) {
55385            return Ok(Some(Expression::TruncateTable(Box::new(TruncateTable {
55386                expressions: Vec::new(),
55387                is_database: None,
55388                exists: false,
55389                only: None,
55390                cluster: None,
55391                identity: None,
55392                option: None,
55393                partition: None,
55394            }))));
55395        }
55396        if self.match_text_seq(&["CONTINUE", "IDENTITY"]) {
55397            // Matched: CONTINUE IDENTITY
55398            return Ok(None);
55399        }
55400        if self.match_text_seq(&["CASCADE"]) {
55401            // Matched: CASCADE
55402            return Ok(None);
55403        }
55404        Ok(None)
55405    }
55406
55407    /// parse_ttl - Implemented from Python _parse_ttl
55408    /// Parses ClickHouse TTL expression with optional DELETE, RECOMPRESS, TO DISK/VOLUME
55409    pub fn parse_ttl(&mut self) -> Result<Option<Expression>> {
55410        // Parse CSV of TTL actions
55411        let mut expressions = Vec::new();
55412
55413        loop {
55414            // Parse the base expression
55415            let base_start = self.current;
55416            let this = match self.parse_bitwise() {
55417                Ok(Some(expr)) => expr,
55418                _ => {
55419                    self.current = base_start;
55420                    let mut paren_depth = 0usize;
55421                    while !self.is_at_end() {
55422                        if paren_depth == 0
55423                            && (self.check(TokenType::Comma)
55424                                || self.peek().text.eq_ignore_ascii_case("DELETE")
55425                                || self.peek().text.eq_ignore_ascii_case("RECOMPRESS")
55426                                || self.peek().text.eq_ignore_ascii_case("TO")
55427                                || self.peek().text.eq_ignore_ascii_case("WHERE")
55428                                || self.peek().text.eq_ignore_ascii_case("GROUP")
55429                                || self.peek().text.eq_ignore_ascii_case("SET"))
55430                        {
55431                            break;
55432                        }
55433                        if self.check(TokenType::LParen) {
55434                            paren_depth += 1;
55435                        } else if self.check(TokenType::RParen) {
55436                            if paren_depth == 0 {
55437                                break;
55438                            }
55439                            paren_depth -= 1;
55440                        }
55441                        self.skip();
55442                    }
55443                    if self.current == base_start {
55444                        break;
55445                    }
55446                    let raw = self
55447                        .tokens_to_sql(base_start, self.current)
55448                        .trim()
55449                        .to_string();
55450                    Expression::Var(Box::new(Var { this: raw }))
55451                }
55452            };
55453
55454            // Check for TTL action
55455            let action = if self.match_text_seq(&["DELETE"]) {
55456                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
55457                    this: Box::new(this),
55458                    delete: Some(Box::new(Expression::Boolean(BooleanLiteral {
55459                        value: true,
55460                    }))),
55461                    recompress: None,
55462                    to_disk: None,
55463                    to_volume: None,
55464                }))
55465            } else if self.match_text_seq(&["RECOMPRESS"]) {
55466                let recompress = if self.match_identifier("CODEC") {
55467                    self.expect(TokenType::LParen)?;
55468                    let mut args = Vec::new();
55469                    if !self.check(TokenType::RParen) {
55470                        args.push(self.parse_expression()?);
55471                        while self.match_token(TokenType::Comma) {
55472                            args.push(self.parse_expression()?);
55473                        }
55474                    }
55475                    self.expect(TokenType::RParen)?;
55476                    Some(Box::new(Expression::Function(Box::new(Function::new(
55477                        "CODEC".to_string(),
55478                        args,
55479                    )))))
55480                } else {
55481                    self.parse_bitwise()?.map(Box::new)
55482                };
55483                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
55484                    this: Box::new(this),
55485                    delete: None,
55486                    recompress,
55487                    to_disk: None,
55488                    to_volume: None,
55489                }))
55490            } else if self.match_text_seq(&["TO", "DISK"]) {
55491                let to_disk = self.parse_string()?.map(Box::new);
55492                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
55493                    this: Box::new(this),
55494                    delete: None,
55495                    recompress: None,
55496                    to_disk,
55497                    to_volume: None,
55498                }))
55499            } else if self.match_text_seq(&["TO", "VOLUME"]) {
55500                let to_volume = self.parse_string()?.map(Box::new);
55501                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
55502                    this: Box::new(this),
55503                    delete: None,
55504                    recompress: None,
55505                    to_disk: None,
55506                    to_volume,
55507                }))
55508            } else {
55509                this
55510            };
55511
55512            expressions.push(action);
55513
55514            if !self.match_token(TokenType::Comma) {
55515                break;
55516            }
55517        }
55518
55519        // Parse optional top-level WHERE clause (for backwards compatibility)
55520        let where_ = self.parse_where()?.map(Box::new);
55521
55522        // Parse optional GROUP BY
55523        let group = if self.match_token(TokenType::Group) {
55524            self.expect(TokenType::By)?;
55525            let mut exprs = Vec::new();
55526            exprs.push(self.parse_expression()?);
55527            while self.match_token(TokenType::Comma) {
55528                exprs.push(self.parse_expression()?);
55529            }
55530            Some(Box::new(Expression::Group(Box::new(Group {
55531                expressions: exprs,
55532                grouping_sets: None,
55533                cube: None,
55534                rollup: None,
55535                totals: None,
55536                all: None,
55537            }))))
55538        } else {
55539            None
55540        };
55541
55542        // Parse optional SET (aggregates) after GROUP BY
55543        let aggregates = if group.is_some() && self.match_token(TokenType::Set) {
55544            let mut aggs = Vec::new();
55545            loop {
55546                aggs.push(self.parse_expression()?);
55547                if !self.match_token(TokenType::Comma) {
55548                    break;
55549                }
55550            }
55551            if aggs.is_empty() {
55552                None
55553            } else {
55554                Some(Box::new(Expression::Tuple(Box::new(Tuple {
55555                    expressions: aggs,
55556                }))))
55557            }
55558        } else {
55559            None
55560        };
55561
55562        Ok(Some(Expression::MergeTreeTTL(Box::new(MergeTreeTTL {
55563            expressions,
55564            where_,
55565            group,
55566            aggregates,
55567        }))))
55568    }
55569
55570    /// parse_type - Parses a data type expression
55571    /// Python: _parse_type
55572    pub fn parse_type(&mut self) -> Result<Option<Expression>> {
55573        // First try to parse an interval
55574        if let Some(interval) = self.parse_interval()? {
55575            return self.parse_column_ops_with_expr(Some(interval));
55576        }
55577
55578        // Try to parse a data type
55579        let data_type = self.parse_types()?;
55580
55581        if let Some(dt) = data_type {
55582            // If it's a Cast (BigQuery inline constructor), apply column ops
55583            if matches!(dt, Expression::Cast(_)) {
55584                return self.parse_column_ops_with_expr(Some(dt));
55585            }
55586
55587            // Try to parse a primary expression after the type
55588            let start_pos = self.current;
55589            if let Some(primary) = self.parse_primary_or_var()? {
55590                // If it's a literal, this might be a type cast like DATE '2020-01-01'
55591                if let Expression::Literal(_) = &primary {
55592                    let result = self.parse_column_ops_with_expr(Some(primary))?;
55593                    if let Some(value) = result {
55594                        // Create a Cast expression
55595                        if let Expression::DataType(data_type_struct) = dt {
55596                            return Ok(Some(Expression::Cast(Box::new(Cast {
55597                                this: value,
55598                                to: data_type_struct,
55599                                trailing_comments: Vec::new(),
55600                                double_colon_syntax: false,
55601                                format: None,
55602                                default: None,
55603                                inferred_type: None,
55604                            }))));
55605                        }
55606                    }
55607                }
55608                // Backtrack if not a type-literal pattern
55609                self.current = start_pos;
55610            }
55611
55612            return Ok(Some(dt));
55613        }
55614
55615        Ok(None)
55616    }
55617
55618    /// parse_type_size - Ported from Python _parse_type_size
55619    /// Parses type size parameters like 10 in VARCHAR(10) or 10, 2 in DECIMAL(10, 2)
55620    #[allow(unused_variables, unused_mut)]
55621    pub fn parse_type_size(&mut self) -> Result<Option<Expression>> {
55622        // First try to parse a type - this handles both numeric literals and type names
55623        let this = self.parse_type()?;
55624
55625        if this.is_none() {
55626            return Ok(None);
55627        }
55628
55629        let mut result = this.unwrap();
55630
55631        // If it's a Column with no table, convert it to an Identifier (var)
55632        // This handles cases like CHAR in VARCHAR(100 CHAR)
55633        if let Expression::Column(ref col) = result {
55634            if col.table.is_none() {
55635                result = Expression::Identifier(col.name.clone());
55636            }
55637        }
55638
55639        // Check for optional expression after the type (e.g., "CHAR" in "100 CHAR")
55640        // This is for byte/char length specifiers in some dialects
55641        if let Some(var_token) = self.parse_var()? {
55642            // We have an additional specifier, combine them
55643            // For now, just return the original result since Rust doesn't have DataTypeParam
55644            // The var expression would be attached as an expression in Python
55645        }
55646
55647        Ok(Some(result))
55648    }
55649
55650    /// parse_types - Implemented from Python _parse_types
55651    /// Calls: parse_string
55652    #[allow(unused_variables, unused_mut)]
55653    pub fn parse_types(&mut self) -> Result<Option<Expression>> {
55654        if self.match_text_seq(&["SYSUDTLIB", "."]) {
55655            return Ok(Some(Expression::Identifier(Identifier {
55656                name: String::new(),
55657                quoted: false,
55658                trailing_comments: Vec::new(),
55659                span: None,
55660            })));
55661        }
55662        if self.match_text_seq(&["WITH", "TIME", "ZONE"]) {
55663            // Matched: WITH TIME ZONE
55664            return Ok(None);
55665        }
55666        if self.match_text_seq(&["WITH", "LOCAL", "TIME", "ZONE"]) {
55667            // Matched: WITH LOCAL TIME ZONE
55668            return Ok(None);
55669        }
55670        Ok(None)
55671    }
55672
55673    /// parse_unique - Implemented from Python _parse_unique
55674    /// Parses UNIQUE [KEY|INDEX] [NULLS NOT DISTINCT] [(columns)] [USING index_type]
55675    #[allow(unused_variables, unused_mut)]
55676    pub fn parse_unique(&mut self) -> Result<Option<Expression>> {
55677        // Check for optional KEY/INDEX
55678        let _ = self.match_texts(&["KEY", "INDEX"]);
55679
55680        // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
55681        let nulls = if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
55682            Some(Box::new(Expression::Boolean(BooleanLiteral {
55683                value: true,
55684            })))
55685        } else {
55686            None
55687        };
55688
55689        // Parse the optional key name and schema (column list)
55690        let unique_key = self.parse_unique_key()?;
55691        let this = self.parse_schema_with_this(unique_key)?;
55692
55693        // Parse optional USING index_type
55694        let index_type = if self.match_token(TokenType::Using) {
55695            self.skip();
55696            Some(Box::new(Expression::Var(Box::new(Var {
55697                this: self.previous().text.clone(),
55698            }))))
55699        } else {
55700            None
55701        };
55702
55703        Ok(Some(Expression::UniqueColumnConstraint(Box::new(
55704            UniqueColumnConstraint {
55705                this: this.map(Box::new),
55706                index_type,
55707                on_conflict: None,
55708                nulls,
55709                options: Vec::new(),
55710            },
55711        ))))
55712    }
55713
55714    /// parse_unique_key - Parse the key/index name for UNIQUE constraint
55715    /// Simply parses an identifier
55716    #[allow(unused_variables, unused_mut)]
55717    pub fn parse_unique_key(&mut self) -> Result<Option<Expression>> {
55718        self.parse_id_var()
55719    }
55720
55721    /// parse_unnest - Ported from Python _parse_unnest
55722    /// Parses UNNEST(array_expr) [WITH ORDINALITY] [AS alias]
55723    #[allow(unused_variables, unused_mut)]
55724    pub fn parse_unnest(&mut self) -> Result<Option<Expression>> {
55725        // Check for UNNEST keyword
55726        if !self.match_texts(&["UNNEST"]) {
55727            return Ok(None);
55728        }
55729
55730        // Expect opening parenthesis
55731        if !self.match_token(TokenType::LParen) {
55732            return Ok(None);
55733        }
55734
55735        // Parse comma-separated array expression(s): UNNEST(arr1, arr2, ...)
55736        let this = match self.parse_expression() {
55737            Ok(expr) => expr,
55738            Err(e) => return Err(e),
55739        };
55740
55741        let mut extra_expressions = Vec::new();
55742        while self.match_token(TokenType::Comma) {
55743            let expr = self.parse_expression()?;
55744            extra_expressions.push(expr);
55745        }
55746
55747        // Expect closing parenthesis
55748        self.expect(TokenType::RParen)?;
55749
55750        // Check for WITH ORDINALITY (Presto) or WITH OFFSET (BigQuery)
55751        let mut with_ordinality = self.match_text_seq(&["WITH", "ORDINALITY"]);
55752        let mut offset_alias = None;
55753        if !with_ordinality && self.match_text_seq(&["WITH", "OFFSET"]) {
55754            with_ordinality = true;
55755            // Parse optional offset alias: WITH OFFSET AS y or WITH OFFSET y
55756            if matches!(
55757                self.config.dialect,
55758                Some(crate::dialects::DialectType::BigQuery)
55759            ) {
55760                let has_as = self.match_token(TokenType::As);
55761                if has_as || self.check(TokenType::Identifier) || self.check(TokenType::Var) {
55762                    let alias_name = self.advance().text;
55763                    offset_alias = Some(crate::expressions::Identifier {
55764                        name: alias_name,
55765                        quoted: false,
55766                        trailing_comments: Vec::new(),
55767                        span: None,
55768                    });
55769                }
55770            }
55771        }
55772
55773        // Parse optional alias
55774        let alias = if self.match_token(TokenType::As)
55775            || self.check(TokenType::Identifier)
55776            || self.check(TokenType::QuotedIdentifier)
55777        {
55778            if self.check(TokenType::Identifier) || self.check(TokenType::QuotedIdentifier) {
55779                let is_quoted = self.check(TokenType::QuotedIdentifier);
55780                let token = self.advance();
55781                let mut ident = Identifier::new(token.text.clone());
55782                if is_quoted {
55783                    ident.quoted = true;
55784                }
55785                Some(ident)
55786            } else {
55787                None
55788            }
55789        } else {
55790            None
55791        };
55792
55793        Ok(Some(Expression::Unnest(Box::new(UnnestFunc {
55794            this,
55795            expressions: extra_expressions,
55796            with_ordinality,
55797            alias,
55798            offset_alias,
55799        }))))
55800    }
55801
55802    /// parse_unpivot_columns - Implemented from Python _parse_unpivot_columns
55803    /// Python: parser.py:4454-4462
55804    /// Parses INTO NAME column VALUE col1, col2, ...
55805    #[allow(unused_variables, unused_mut)]
55806    pub fn parse_unpivot_columns(&mut self) -> Result<Option<Expression>> {
55807        // Must match INTO keyword
55808        if !self.match_token(TokenType::Into) {
55809            return Ok(None);
55810        }
55811
55812        // Parse NAME column
55813        let this = if self.match_text_seq(&["NAME"]) {
55814            self.parse_column()?
55815        } else {
55816            None
55817        };
55818
55819        // Parse VALUE columns
55820        let expressions = if self.match_text_seq(&["VALUE"]) {
55821            let mut cols = Vec::new();
55822            loop {
55823                if let Some(col) = self.parse_column()? {
55824                    cols.push(col);
55825                }
55826                if !self.match_token(TokenType::Comma) {
55827                    break;
55828                }
55829            }
55830            cols
55831        } else {
55832            Vec::new()
55833        };
55834
55835        // If we have either this or expressions, return an UnpivotColumns
55836        if this.is_some() || !expressions.is_empty() {
55837            Ok(Some(Expression::UnpivotColumns(Box::new(UnpivotColumns {
55838                this: Box::new(this.unwrap_or(Expression::Null(Null))),
55839                expressions,
55840            }))))
55841        } else {
55842            Ok(None)
55843        }
55844    }
55845
55846    /// parse_unquoted_field - Parses a field and converts unquoted identifiers to Var
55847    /// Python: _parse_unquoted_field
55848    pub fn parse_unquoted_field(&mut self) -> Result<Option<Expression>> {
55849        let field = self.parse_field()?;
55850
55851        // If field is an unquoted identifier, convert it to a Var
55852        match field {
55853            Some(Expression::Identifier(id)) if !id.quoted => {
55854                Ok(Some(Expression::Var(Box::new(Var { this: id.name }))))
55855            }
55856            other => Ok(other),
55857        }
55858    }
55859
55860    /// parse_user_defined_function - Parses user-defined function call
55861    /// Python: _parse_user_defined_function
55862    /// Parses: schema.function_name(param1, param2, ...)
55863    pub fn parse_user_defined_function(&mut self) -> Result<Option<Expression>> {
55864        // Parse table parts (potentially schema-qualified function name)
55865        let this = self.parse_table_parts()?;
55866        if this.is_none() {
55867            return Ok(None);
55868        }
55869
55870        // If no L_PAREN, return just the table parts (not a function call)
55871        if !self.match_token(TokenType::LParen) {
55872            return Ok(this);
55873        }
55874
55875        // Parse function parameters
55876        let mut expressions = Vec::new();
55877        if !self.check(TokenType::RParen) {
55878            loop {
55879                if let Some(param) = self.parse_function_parameter()? {
55880                    expressions.push(param);
55881                }
55882                if !self.match_token(TokenType::Comma) {
55883                    break;
55884                }
55885            }
55886        }
55887
55888        self.match_token(TokenType::RParen);
55889
55890        Ok(Some(Expression::UserDefinedFunction(Box::new(
55891            UserDefinedFunction {
55892                this: Box::new(this.unwrap()),
55893                expressions,
55894                wrapped: Some(Box::new(Expression::Boolean(BooleanLiteral {
55895                    value: true,
55896                }))),
55897            },
55898        ))))
55899    }
55900
55901    /// parse_user_defined_function_expression - Parse user-defined function expression
55902    #[allow(unused_variables, unused_mut)]
55903    pub fn parse_user_defined_function_expression(&mut self) -> Result<Option<Expression>> {
55904        // Parse a statement and wrap in Some if successful
55905        match self.parse_statement() {
55906            Ok(stmt) => Ok(Some(stmt)),
55907            Err(_) => Ok(None),
55908        }
55909    }
55910
55911    /// parse_user_defined_type - Parses a user-defined type reference
55912    /// Python: _parse_user_defined_type
55913    /// Format: schema.type_name or just type_name
55914    pub fn parse_user_defined_type(
55915        &mut self,
55916        identifier: Identifier,
55917    ) -> Result<Option<Expression>> {
55918        let mut type_name = identifier.name.clone();
55919
55920        // Handle dotted names (schema.type_name)
55921        while self.match_token(TokenType::Dot) {
55922            if !self.is_at_end() {
55923                let token = self.advance();
55924                type_name = format!("{}.{}", type_name, token.text);
55925            } else {
55926                break;
55927            }
55928        }
55929
55930        // Return as a custom data type
55931        Ok(Some(Expression::DataType(DataType::Custom {
55932            name: type_name,
55933        })))
55934    }
55935
55936    /// parse_using_identifiers - Ported from Python _parse_using_identifiers
55937    /// Parses (col1, col2, ...) for JOIN USING clause
55938    #[allow(unused_variables, unused_mut)]
55939    pub fn parse_using_identifiers(&mut self) -> Result<Option<Expression>> {
55940        // Optionally expect opening paren
55941        let has_paren = self.match_token(TokenType::LParen);
55942
55943        let mut identifiers = Vec::new();
55944        loop {
55945            // Parse column as identifier
55946            if let Some(expr) = self.parse_identifier()? {
55947                identifiers.push(expr);
55948            } else {
55949                break;
55950            }
55951            if !self.match_token(TokenType::Comma) {
55952                break;
55953            }
55954        }
55955
55956        // Match closing paren if we matched opening
55957        if has_paren {
55958            self.expect(TokenType::RParen)?;
55959        }
55960
55961        if identifiers.is_empty() {
55962            Ok(None)
55963        } else {
55964            Ok(Some(Expression::Tuple(Box::new(Tuple {
55965                expressions: identifiers,
55966            }))))
55967        }
55968    }
55969
55970    /// parse_value - Parses a value tuple for INSERT VALUES clause
55971    /// Python: _parse_value
55972    /// Syntax: (expr1, expr2, ...) or just expr (single value)
55973    pub fn parse_value(&mut self) -> Result<Option<Expression>> {
55974        // Check for parenthesized list of expressions
55975        if self.match_token(TokenType::LParen) {
55976            let mut expressions = Vec::new();
55977
55978            if !self.check(TokenType::RParen) {
55979                loop {
55980                    // Support DEFAULT keyword in VALUES
55981                    if self.match_texts(&["DEFAULT"]) {
55982                        let text = self.previous().text.to_ascii_uppercase();
55983                        expressions.push(Expression::Var(Box::new(Var { this: text })));
55984                    } else {
55985                        // Try to parse an expression
55986                        let saved_pos = self.current;
55987                        match self.parse_expression() {
55988                            Ok(expr) => expressions.push(expr),
55989                            Err(_) => {
55990                                self.current = saved_pos;
55991                            }
55992                        }
55993                    }
55994
55995                    if !self.match_token(TokenType::Comma) {
55996                        break;
55997                    }
55998                }
55999            }
56000
56001            self.match_token(TokenType::RParen);
56002            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
56003        }
56004
56005        // Single value without parentheses (some dialects support VALUES 1, 2)
56006        let saved_pos = self.current;
56007        match self.parse_expression() {
56008            Ok(expr) => {
56009                return Ok(Some(Expression::Tuple(Box::new(Tuple {
56010                    expressions: vec![expr],
56011                }))));
56012            }
56013            Err(_) => {
56014                self.current = saved_pos;
56015            }
56016        }
56017
56018        Ok(None)
56019    }
56020
56021    /// parse_var - Parse variable reference (unquoted identifier)
56022    /// Python: if self._match(TokenType.VAR): return exp.Var(this=self._prev.text)
56023    pub fn parse_var(&mut self) -> Result<Option<Expression>> {
56024        if self.match_token(TokenType::Var) {
56025            let text = self.previous().text.clone();
56026            return Ok(Some(Expression::Var(Box::new(Var { this: text }))));
56027        }
56028        // Fall back to placeholder parsing
56029        self.parse_placeholder()
56030    }
56031
56032    /// parse_var_from_options - Ported from Python _parse_var_from_options
56033    /// Parses a variable/identifier from a predefined set of options
56034    #[allow(unused_variables, unused_mut)]
56035    pub fn parse_var_from_options(&mut self) -> Result<Option<Expression>> {
56036        // Without the options dict, we just try to parse an identifier
56037        if self.is_at_end() {
56038            return Ok(None);
56039        }
56040
56041        // Get current token text as the option
56042        let token = self.peek().clone();
56043        if token.token_type == TokenType::Identifier || token.token_type == TokenType::Var {
56044            self.skip();
56045            return Ok(Some(Expression::Var(Box::new(Var {
56046                this: token.text.to_ascii_uppercase(),
56047            }))));
56048        }
56049
56050        Ok(None)
56051    }
56052
56053    /// parse_var_or_string - Delegates to parse_string
56054    #[allow(unused_variables, unused_mut)]
56055    /// parse_var_or_string - Parses a string literal or a variable
56056    /// Python: parser.py:7506-7507
56057    pub fn parse_var_or_string(&mut self) -> Result<Option<Expression>> {
56058        // Try string first, then var
56059        if let Some(s) = self.parse_string()? {
56060            return Ok(Some(s));
56061        }
56062        self.parse_var_any_token()
56063    }
56064
56065    /// parse_vector_expressions - Transforms vector type parameters
56066    /// Python: _parse_vector_expressions
56067    /// In Python, this transforms a list of expressions where the first element (identifier)
56068    /// is converted to a DataType. In Rust, since VECTOR type parsing is handled inline in
56069    /// parse_data_type, this method parses vector expressions (element_type, dimension) from
56070    /// the current position and returns them as a Tuple.
56071    pub fn parse_vector_expressions(&mut self) -> Result<Option<Expression>> {
56072        let mut expressions = Vec::new();
56073
56074        // Parse element type - convert identifier to DataType
56075        if let Some(type_expr) = self.parse_type()? {
56076            expressions.push(type_expr);
56077        } else {
56078            return Ok(None);
56079        }
56080
56081        // Parse optional dimension or additional parameters
56082        while self.match_token(TokenType::Comma) {
56083            if let Some(expr) = self.parse_primary_or_var()? {
56084                expressions.push(expr);
56085            }
56086        }
56087
56088        if expressions.is_empty() {
56089            return Ok(None);
56090        }
56091
56092        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
56093    }
56094
56095    /// parse_version - Implemented from Python _parse_version
56096    /// Python: parser.py:4266-4295
56097    /// Parses FOR SYSTEM_TIME AS OF, VERSIONS BETWEEN, etc.
56098    #[allow(unused_variables, unused_mut)]
56099    pub fn parse_version(&mut self) -> Result<Option<Expression>> {
56100        // Check for TIMESTAMP or VERSION snapshot token
56101        let this = if self.match_token(TokenType::TimestampSnapshot) {
56102            "TIMESTAMP".to_string()
56103        } else if self.match_token(TokenType::VersionSnapshot) {
56104            "VERSION".to_string()
56105        } else {
56106            return Ok(None);
56107        };
56108
56109        // Parse the kind and expression
56110        let (kind, expression) = if self.match_texts(&["FROM", "BETWEEN"]) {
56111            // FROM start TO end or BETWEEN start AND end
56112            let kind_str = self.previous().text.to_ascii_uppercase();
56113            let start = self.parse_bitwise()?;
56114            self.match_texts(&["TO", "AND"]);
56115            let end = self.parse_bitwise()?;
56116            let tuple = Expression::Tuple(Box::new(Tuple {
56117                expressions: vec![
56118                    start.unwrap_or(Expression::Null(Null)),
56119                    end.unwrap_or(Expression::Null(Null)),
56120                ],
56121            }));
56122            (kind_str, Some(Box::new(tuple)))
56123        } else if self.match_text_seq(&["CONTAINED", "IN"]) {
56124            // CONTAINED IN (values)
56125            let expressions = if self.match_token(TokenType::LParen) {
56126                let exprs = self.parse_expression_list()?;
56127                self.expect(TokenType::RParen)?;
56128                exprs
56129            } else {
56130                Vec::new()
56131            };
56132            (
56133                "CONTAINED IN".to_string(),
56134                Some(Box::new(Expression::Tuple(Box::new(Tuple { expressions })))),
56135            )
56136        } else if self.match_token(TokenType::All) {
56137            // ALL
56138            ("ALL".to_string(), None)
56139        } else {
56140            // AS OF
56141            self.match_text_seq(&["AS", "OF"]);
56142            let type_expr = self.parse_type()?;
56143            ("AS OF".to_string(), type_expr.map(Box::new))
56144        };
56145
56146        Ok(Some(Expression::Version(Box::new(Version {
56147            this: Box::new(Expression::Var(Box::new(Var { this }))),
56148            kind,
56149            expression,
56150        }))))
56151    }
56152
56153    /// parse_volatile_property - Parses VOLATILE property
56154    /// Python: _parse_volatile_property
56155    /// Returns VolatileProperty for table volatility or StabilityProperty for function stability
56156    pub fn parse_volatile_property(&mut self) -> Result<Option<Expression>> {
56157        // Check the token before VOLATILE to determine context
56158        // In SQL, VOLATILE can mean:
56159        // 1. Table volatility (CREATE VOLATILE TABLE)
56160        // 2. Function stability (CREATE FUNCTION ... VOLATILE)
56161
56162        // Look back to see if this is in a table context
56163        // PRE_VOLATILE_TOKENS typically include: CREATE, REPLACE, GLOBAL, etc.
56164        let is_table_context = if self.current >= 2 {
56165            let pre_token = &self.tokens[self.current - 2];
56166            matches!(
56167                pre_token.token_type,
56168                TokenType::Create | TokenType::Global | TokenType::Temporary | TokenType::Replace
56169            )
56170        } else {
56171            false
56172        };
56173
56174        if is_table_context {
56175            Ok(Some(Expression::VolatileProperty(Box::new(
56176                VolatileProperty { this: None },
56177            ))))
56178        } else {
56179            // Function stability - return StabilityProperty with "VOLATILE" literal
56180            Ok(Some(Expression::StabilityProperty(Box::new(
56181                StabilityProperty {
56182                    this: Box::new(Expression::Literal(Box::new(Literal::String(
56183                        "VOLATILE".to_string(),
56184                    )))),
56185                },
56186            ))))
56187        }
56188    }
56189
56190    /// parse_when_matched - Implemented from Python _parse_when_matched
56191    /// Calls: parse_disjunction, parse_star, parse_value
56192    #[allow(unused_variables, unused_mut)]
56193    /// Parse WHEN [NOT] MATCHED clauses for MERGE statements
56194    /// This is the public entry point that calls parse_when_matched_clauses
56195    pub fn parse_when_matched(&mut self) -> Result<Option<Expression>> {
56196        self.parse_when_matched_clauses()
56197    }
56198
56199    /// parse_where - Parse WHERE clause
56200    /// Python: if not self._match(TokenType.WHERE): return None; return exp.Where(this=self._parse_disjunction())
56201    pub fn parse_where(&mut self) -> Result<Option<Expression>> {
56202        if !self.match_token(TokenType::Where) {
56203            return Ok(None);
56204        }
56205        // Parse the condition expression
56206        let condition = self.parse_expression()?;
56207        Ok(Some(Expression::Where(Box::new(Where { this: condition }))))
56208    }
56209
56210    /// parse_window - Implemented from Python _parse_window
56211    /// Calls: parse_window_spec, parse_partition_and_order
56212    #[allow(unused_variables, unused_mut)]
56213    pub fn parse_window(&mut self) -> Result<Option<Expression>> {
56214        if self.match_text_seq(&["WITHIN", "GROUP"]) {
56215            return Ok(Some(Expression::WindowSpec(Box::new(WindowSpec {
56216                partition_by: Vec::new(),
56217                order_by: Vec::new(),
56218                frame: None,
56219            }))));
56220        }
56221        if self.match_text_seq(&["LAST"]) {
56222            // Matched: LAST
56223            return Ok(None);
56224        }
56225        if self.match_text_seq(&["EXCLUDE"]) {
56226            // Matched: EXCLUDE
56227            return Ok(None);
56228        }
56229        Ok(None)
56230    }
56231
56232    /// parse_window_clause - Ported from Python _parse_window_clause
56233    /// Parses WINDOW named_window_definition [, named_window_definition, ...]
56234    #[allow(unused_variables, unused_mut)]
56235    pub fn parse_window_clause(&mut self) -> Result<Option<Expression>> {
56236        if !self.match_token(TokenType::Window) {
56237            return Ok(None);
56238        }
56239
56240        // Parse comma-separated named window definitions
56241        let mut windows = Vec::new();
56242        loop {
56243            // Parse window name
56244            let name = self.parse_identifier()?;
56245            if name.is_none() {
56246                break;
56247            }
56248
56249            // Expect AS
56250            self.expect(TokenType::As)?;
56251
56252            // Parse window specification (parenthesized)
56253            self.expect(TokenType::LParen)?;
56254            let spec = self.parse_window_spec_inner()?;
56255            self.expect(TokenType::RParen)?;
56256
56257            if let (Some(name_expr), Some(spec_expr)) = (name, spec) {
56258                // Create an Alias expression wrapping the spec with the name
56259                let alias_ident = if let Expression::Identifier(id) = name_expr {
56260                    id
56261                } else {
56262                    Identifier::new("window")
56263                };
56264                windows.push(Expression::Alias(Box::new(Alias {
56265                    this: spec_expr,
56266                    alias: alias_ident,
56267                    column_aliases: Vec::new(),
56268                    pre_alias_comments: Vec::new(),
56269                    trailing_comments: Vec::new(),
56270                    inferred_type: None,
56271                })));
56272            }
56273
56274            if !self.match_token(TokenType::Comma) {
56275                break;
56276            }
56277        }
56278
56279        if windows.is_empty() {
56280            Ok(None)
56281        } else {
56282            Ok(Some(Expression::Tuple(Box::new(Tuple {
56283                expressions: windows,
56284            }))))
56285        }
56286    }
56287
56288    /// Parse window spec inner (without parentheses)
56289    fn parse_window_spec_inner(&mut self) -> Result<Option<Expression>> {
56290        // Parse optional base window name (identifier not followed by PARTITION or ORDER or DISTRIBUTE or SORT)
56291        let _base = if (self.check(TokenType::Identifier)
56292            || self.check(TokenType::QuotedIdentifier))
56293            && !self.check(TokenType::Partition)
56294            && !self.check(TokenType::Order)
56295            && !self.check(TokenType::Distribute)
56296            && !self.check(TokenType::Sort)
56297        {
56298            self.parse_identifier()?
56299        } else {
56300            None
56301        };
56302
56303        // Parse PARTITION BY or DISTRIBUTE BY (Hive uses DISTRIBUTE BY in window specs)
56304        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
56305            self.parse_expression_list()?
56306        } else if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
56307            // Hive: DISTRIBUTE BY is equivalent to PARTITION BY in window specs
56308            self.parse_expression_list()?
56309        } else {
56310            Vec::new()
56311        };
56312
56313        // Parse ORDER BY or SORT BY (Hive uses SORT BY in window specs)
56314        let order_by = if self.match_token(TokenType::Order) {
56315            self.match_token(TokenType::By);
56316            let mut orders = Vec::new();
56317            loop {
56318                if let Some(ordered) = self.parse_ordered_item()? {
56319                    orders.push(ordered);
56320                } else {
56321                    break;
56322                }
56323                if !self.match_token(TokenType::Comma) {
56324                    break;
56325                }
56326            }
56327            orders
56328        } else if self.match_token(TokenType::Sort) {
56329            // Hive: SORT BY is equivalent to ORDER BY in window specs
56330            self.match_token(TokenType::By);
56331            let mut orders = Vec::new();
56332            loop {
56333                if let Some(ordered) = self.parse_ordered_item()? {
56334                    orders.push(ordered);
56335                } else {
56336                    break;
56337                }
56338                if !self.match_token(TokenType::Comma) {
56339                    break;
56340                }
56341            }
56342            orders
56343        } else {
56344            Vec::new()
56345        };
56346
56347        // Parse frame specification (ROWS/RANGE/GROUPS BETWEEN ... AND ...)
56348        let frame = self.parse_window_frame()?;
56349
56350        Ok(Some(Expression::WindowSpec(Box::new(WindowSpec {
56351            partition_by,
56352            order_by,
56353            frame,
56354        }))))
56355    }
56356
56357    /// parse_window_spec - Implemented from Python _parse_window_spec
56358    #[allow(unused_variables, unused_mut)]
56359    pub fn parse_window_spec(&mut self) -> Result<Option<Expression>> {
56360        if self.match_text_seq(&["UNBOUNDED"]) {
56361            // Matched: UNBOUNDED
56362            return Ok(None);
56363        }
56364        if self.match_text_seq(&["CURRENT", "ROW"]) {
56365            // Matched: CURRENT ROW
56366            return Ok(None);
56367        }
56368        Ok(None)
56369    }
56370
56371    /// parse_with_operator - Parse column with operator class (PostgreSQL)
56372    /// Parses: ordered_expression [WITH operator]
56373    #[allow(unused_variables, unused_mut)]
56374    pub fn parse_with_operator(&mut self) -> Result<Option<Expression>> {
56375        // First parse an ordered expression with optional operator class
56376        let this = if let Some(opclass) = self.parse_opclass()? {
56377            opclass
56378        } else if let Some(ordered) = self.parse_ordered()? {
56379            ordered
56380        } else {
56381            return Ok(None);
56382        };
56383
56384        // Check for WITH operator
56385        if !self.match_token(TokenType::With) {
56386            return Ok(Some(this));
56387        }
56388
56389        // Parse the operator
56390        let op = self.parse_var()?;
56391        let op_str = match op {
56392            Some(Expression::Identifier(id)) => id.name,
56393            Some(Expression::Var(v)) => v.this.clone(),
56394            _ => String::new(),
56395        };
56396
56397        Ok(Some(Expression::WithOperator(Box::new(WithOperator {
56398            this: Box::new(this),
56399            op: op_str,
56400        }))))
56401    }
56402
56403    /// parse_with_property - Implemented from Python _parse_with_property
56404    /// Calls: parse_withjournaltable, parse_withisolatedloading, parse_wrapped_properties
56405    #[allow(unused_variables, unused_mut)]
56406    pub fn parse_with_property(&mut self) -> Result<Option<Expression>> {
56407        if self.match_text_seq(&["(", "SYSTEM_VERSIONING"]) {
56408            return Ok(Some(Expression::WithProcedureOptions(Box::new(
56409                WithProcedureOptions {
56410                    expressions: Vec::new(),
56411                },
56412            ))));
56413        }
56414        if self.match_text_seq(&["JOURNAL"]) {
56415            // Matched: JOURNAL
56416            return Ok(None);
56417        }
56418        if self.match_text_seq(&["DATA"]) {
56419            // Matched: DATA
56420            return Ok(None);
56421        }
56422        Ok(None)
56423    }
56424
56425    /// parse_withdata - Implemented from Python _parse_withdata
56426    #[allow(unused_variables, unused_mut)]
56427    pub fn parse_withdata(&mut self) -> Result<Option<Expression>> {
56428        if self.match_text_seq(&["AND", "STATISTICS"]) {
56429            return Ok(Some(Expression::WithDataProperty(Box::new(
56430                WithDataProperty {
56431                    no: None,
56432                    statistics: None,
56433                },
56434            ))));
56435        }
56436        if self.match_text_seq(&["AND", "NO", "STATISTICS"]) {
56437            // Matched: AND NO STATISTICS
56438            return Ok(None);
56439        }
56440        Ok(None)
56441    }
56442
56443    /// parse_withisolatedloading - Implemented from Python _parse_withisolatedloading
56444    #[allow(unused_variables, unused_mut)]
56445    pub fn parse_withisolatedloading(&mut self) -> Result<Option<Expression>> {
56446        if self.match_text_seq(&["NO"]) {
56447            return Ok(Some(Expression::IsolatedLoadingProperty(Box::new(
56448                IsolatedLoadingProperty {
56449                    no: None,
56450                    concurrent: None,
56451                    target: None,
56452                },
56453            ))));
56454        }
56455        if self.match_text_seq(&["CONCURRENT"]) {
56456            // Matched: CONCURRENT
56457            return Ok(None);
56458        }
56459        Ok(None)
56460    }
56461
56462    /// parse_withjournaltable - Teradata WITH JOURNAL TABLE property
56463    /// Parses: WITH JOURNAL TABLE = table_name
56464    #[allow(unused_variables, unused_mut)]
56465    pub fn parse_withjournaltable(&mut self) -> Result<Option<Expression>> {
56466        // Optionally consume TABLE keyword
56467        self.match_token(TokenType::Table);
56468
56469        // Optionally consume = sign
56470        self.match_token(TokenType::Eq);
56471
56472        // Parse the table reference
56473        let table = self.parse_table_parts()?;
56474        if table.is_none() {
56475            return Ok(None);
56476        }
56477
56478        Ok(Some(Expression::WithJournalTableProperty(Box::new(
56479            WithJournalTableProperty {
56480                this: Box::new(table.unwrap()),
56481            },
56482        ))))
56483    }
56484
56485    /// parse_wrapped - Parses an expression wrapped in parentheses
56486    /// Python: _parse_wrapped(parse_method)
56487    /// This version parses a disjunction (expression) inside parentheses
56488    pub fn parse_wrapped(&mut self) -> Result<Option<Expression>> {
56489        if !self.match_token(TokenType::LParen) {
56490            return Ok(None);
56491        }
56492
56493        let result = self.parse_disjunction()?;
56494        self.match_token(TokenType::RParen);
56495
56496        Ok(result)
56497    }
56498
56499    /// parse_wrapped_csv - Parses comma-separated expressions wrapped in parentheses
56500    /// Python: _parse_wrapped_csv(parse_method)
56501    pub fn parse_wrapped_csv(&mut self) -> Result<Option<Expression>> {
56502        if !self.match_token(TokenType::LParen) {
56503            return Ok(None);
56504        }
56505
56506        let expressions = self.parse_expression_list()?;
56507        self.match_token(TokenType::RParen);
56508
56509        if expressions.is_empty() {
56510            return Ok(None);
56511        }
56512
56513        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
56514    }
56515
56516    /// parse_wrapped_id_vars - Parses comma-separated identifiers wrapped in parentheses
56517    /// Python: _parse_wrapped_id_vars
56518    pub fn parse_wrapped_id_vars(&mut self) -> Result<Option<Expression>> {
56519        if !self.match_token(TokenType::LParen) {
56520            return Ok(None);
56521        }
56522
56523        let mut identifiers = Vec::new();
56524        loop {
56525            if let Some(id) = self.parse_id_var()? {
56526                identifiers.push(id);
56527            } else {
56528                break;
56529            }
56530            if !self.match_token(TokenType::Comma) {
56531                break;
56532            }
56533        }
56534
56535        self.match_token(TokenType::RParen);
56536
56537        if identifiers.is_empty() {
56538            return Ok(None);
56539        }
56540
56541        Ok(Some(Expression::Tuple(Box::new(Tuple {
56542            expressions: identifiers,
56543        }))))
56544    }
56545
56546    /// parse_wrapped_options - Implemented from Python _parse_wrapped_options
56547    /// Parses space-separated properties wrapped in parentheses (for Snowflake STAGE_FILE_FORMAT, etc.)
56548    /// Format: = (KEY=VALUE KEY2=VALUE2 ...)
56549    pub fn parse_wrapped_options(&mut self) -> Result<Option<Expression>> {
56550        // Match optional = before opening paren
56551        self.match_token(TokenType::Eq);
56552
56553        // Expect opening paren
56554        if !self.match_token(TokenType::LParen) {
56555            return Ok(None);
56556        }
56557
56558        // Parse space-separated properties (no comma required between them)
56559        let mut properties = Vec::new();
56560        while !self.check(TokenType::RParen) && !self.is_at_end() {
56561            // Try to parse a property: KEY=VALUE
56562            if let Some(prop) = self.parse_option_property()? {
56563                properties.push(prop);
56564            } else {
56565                break;
56566            }
56567        }
56568
56569        // Expect closing paren
56570        self.match_token(TokenType::RParen);
56571
56572        if properties.is_empty() {
56573            Ok(None)
56574        } else {
56575            Ok(Some(Expression::Tuple(Box::new(Tuple {
56576                expressions: properties,
56577            }))))
56578        }
56579    }
56580
56581    /// Parse a single option property: KEY=VALUE
56582    /// Handles various value types: identifiers, strings, numbers, nested parens like ('') or (val1, val2)
56583    fn parse_option_property(&mut self) -> Result<Option<Expression>> {
56584        // Save position to retreat if this isn't a property
56585        let index = self.current;
56586
56587        // Parse the key (identifier/column name)
56588        // For Snowflake options, keys are identifiers like TYPE, FIELD_DELIMITER, NULL_IF, etc.
56589        let key = if self.check(TokenType::Identifier)
56590            || self.check(TokenType::Var)
56591            || self
56592                .peek()
56593                .text
56594                .chars()
56595                .all(|c| c.is_ascii_alphanumeric() || c == '_')
56596        {
56597            let name = self.peek().text.clone();
56598            self.skip();
56599            Some(Expression::Var(Box::new(Var { this: name })))
56600        } else {
56601            None
56602        };
56603
56604        let key = match key {
56605            Some(k) => k,
56606            None => {
56607                self.current = index;
56608                return Ok(None);
56609            }
56610        };
56611
56612        // Expect =
56613        if !self.match_token(TokenType::Eq) {
56614            self.current = index;
56615            return Ok(None);
56616        }
56617
56618        // Parse the value - can be:
56619        // - Simple identifier: CSV, SKIP_FILE, BASE64, TRUE, FALSE, CASE_SENSITIVE
56620        // - String literal: '|', '"', 'TZHTZM YYYY-MM-DD HH24:MI:SS.FF9'
56621        // - Number: 5
56622        // - Nested parens for tuple: ('')
56623        let value = if self.check(TokenType::LParen) {
56624            // Parse nested parenthesized value like NULL_IF=('')
56625            self.skip(); // consume (
56626            let mut inner_exprs = Vec::new();
56627            while !self.check(TokenType::RParen) && !self.is_at_end() {
56628                if let Some(expr) = self.parse_primary_for_option()? {
56629                    inner_exprs.push(expr);
56630                }
56631                // Allow comma between nested values
56632                self.match_token(TokenType::Comma);
56633            }
56634            self.match_token(TokenType::RParen);
56635            Expression::Tuple(Box::new(Tuple {
56636                expressions: inner_exprs,
56637            }))
56638        } else if let Some(primary) = self.parse_primary_for_option()? {
56639            primary
56640        } else {
56641            // Fallback: try to parse as a var
56642            let text = self.peek().text.clone();
56643            self.skip();
56644            Expression::Var(Box::new(Var { this: text }))
56645        };
56646
56647        // Return as a Property expression (KEY=VALUE)
56648        Ok(Some(Expression::Property(Box::new(Property {
56649            this: Box::new(key),
56650            value: Some(Box::new(value)),
56651        }))))
56652    }
56653
56654    /// Parse a primary value for option properties
56655    /// Handles strings, numbers, identifiers, TRUE/FALSE
56656    fn parse_primary_for_option(&mut self) -> Result<Option<Expression>> {
56657        // String literal
56658        if self.check(TokenType::String) {
56659            let text = self.peek().text.clone();
56660            self.skip();
56661            return Ok(Some(Expression::Literal(Box::new(Literal::String(text)))));
56662        }
56663
56664        // Number
56665        if self.check(TokenType::Number) {
56666            let text = self.peek().text.clone();
56667            self.skip();
56668            return Ok(Some(Expression::Literal(Box::new(Literal::Number(text)))));
56669        }
56670
56671        // TRUE/FALSE
56672        if self.check(TokenType::True) {
56673            self.skip();
56674            return Ok(Some(Expression::Boolean(BooleanLiteral { value: true })));
56675        }
56676        if self.check(TokenType::False) {
56677            self.skip();
56678            return Ok(Some(Expression::Boolean(BooleanLiteral { value: false })));
56679        }
56680
56681        // Identifier or keyword used as value (CSV, SKIP_FILE, BASE64, etc.)
56682        if self.check(TokenType::Identifier)
56683            || self.check(TokenType::Var)
56684            || (!self.check(TokenType::RParen)
56685                && !self.check(TokenType::Comma)
56686                && !self.check(TokenType::Eq)
56687                && !self.is_at_end())
56688        {
56689            let text = self.peek().text.clone();
56690            // Don't consume if it's a closing paren or could be the next property key followed by =
56691            if self.check(TokenType::RParen) {
56692                return Ok(None);
56693            }
56694            // Check if this is the start of next property (followed by =)
56695            if self.check_next(TokenType::Eq) {
56696                return Ok(None);
56697            }
56698            self.skip();
56699            return Ok(Some(Expression::Var(Box::new(Var { this: text }))));
56700        }
56701
56702        Ok(None)
56703    }
56704
56705    /// parse_options_list - Parses BigQuery-style OPTIONS list: (key=value, key=value, ...)
56706    /// Parses key=value assignments where values can be complex expressions
56707    pub fn parse_options_list(&mut self) -> Result<Vec<Expression>> {
56708        // Expect opening paren
56709        if !self.match_token(TokenType::LParen) {
56710            return Ok(Vec::new());
56711        }
56712
56713        // Parse comma-separated key=value pairs
56714        let mut options = Vec::new();
56715        loop {
56716            // Check for empty OPTIONS () or end of list
56717            if self.check(TokenType::RParen) {
56718                break;
56719            }
56720
56721            // Parse key=value using parse_assignment which handles EQ operations
56722            if let Some(opt) = self.parse_assignment()? {
56723                options.push(opt);
56724            } else {
56725                break;
56726            }
56727
56728            if !self.match_token(TokenType::Comma) {
56729                break;
56730            }
56731        }
56732
56733        // Expect closing paren
56734        self.expect(TokenType::RParen)?;
56735
56736        Ok(options)
56737    }
56738
56739    /// Parse BigQuery PARTITION BY property and return a typed AST node.
56740    fn parse_bigquery_partition_by_property(&mut self) -> Result<Option<Expression>> {
56741        let start = self.current;
56742        let matched_partition = if self.match_token(TokenType::PartitionBy) {
56743            true
56744        } else if self.match_token(TokenType::Partition) {
56745            self.match_token(TokenType::By)
56746        } else {
56747            false
56748        };
56749
56750        if !matched_partition {
56751            self.current = start;
56752            return Ok(None);
56753        }
56754
56755        let mut expressions = Vec::new();
56756        while !self.is_at_end()
56757            && !self.check(TokenType::Cluster)
56758            && !self.check(TokenType::As)
56759            && !self.check(TokenType::Semicolon)
56760            && !self.check(TokenType::RParen)
56761            && !self.check_identifier("OPTIONS")
56762        {
56763            match self.parse_expression() {
56764                Ok(expr) => expressions.push(expr),
56765                Err(_) => {
56766                    // Fall back to generic/raw parsing if typed parsing can't consume this form.
56767                    self.current = start;
56768                    return Ok(None);
56769                }
56770            }
56771
56772            if !self.match_token(TokenType::Comma) {
56773                break;
56774            }
56775        }
56776
56777        if expressions.is_empty() {
56778            self.current = start;
56779            return Ok(None);
56780        }
56781
56782        Ok(Some(Expression::PartitionByProperty(Box::new(
56783            PartitionByProperty { expressions },
56784        ))))
56785    }
56786
56787    /// Parse BigQuery CLUSTER BY property and return a typed AST node.
56788    fn parse_bigquery_cluster_by_property(&mut self) -> Result<Option<Expression>> {
56789        let start = self.current;
56790        if !self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
56791            self.current = start;
56792            return Ok(None);
56793        }
56794
56795        let mut columns = Vec::new();
56796        loop {
56797            if let Some(Expression::Identifier(id)) = self.parse_identifier()? {
56798                columns.push(id);
56799            } else if self.is_identifier_or_keyword_token() {
56800                let name = self.advance().text;
56801                columns.push(Identifier {
56802                    name,
56803                    quoted: false,
56804                    trailing_comments: Vec::new(),
56805                    span: None,
56806                });
56807            } else {
56808                // Fall back to generic/raw parsing if typed parsing can't consume this form.
56809                self.current = start;
56810                return Ok(None);
56811            }
56812
56813            if !self.match_token(TokenType::Comma) {
56814                break;
56815            }
56816        }
56817
56818        if columns.is_empty() {
56819            self.current = start;
56820            return Ok(None);
56821        }
56822
56823        Ok(Some(Expression::ClusterByColumnsProperty(Box::new(
56824            ClusterByColumnsProperty { columns },
56825        ))))
56826    }
56827
56828    /// Parse BigQuery OPTIONS (...) clause into typed entries when possible.
56829    /// Falls back to generic `Properties` when options are not simple key/value assignments.
56830    fn parse_bigquery_options_property(&mut self) -> Result<Option<Expression>> {
56831        let start = self.current;
56832        if !self.match_identifier("OPTIONS") {
56833            self.current = start;
56834            return Ok(None);
56835        }
56836
56837        let options = self.parse_options_list()?;
56838        if options.is_empty() {
56839            return Ok(Some(Expression::OptionsProperty(Box::new(
56840                OptionsProperty {
56841                    entries: Vec::new(),
56842                },
56843            ))));
56844        }
56845
56846        let mut entries = Vec::new();
56847        for option_expr in &options {
56848            let Some(entry) = Self::option_entry_from_expression(option_expr) else {
56849                return Ok(Some(Expression::Properties(Box::new(Properties {
56850                    expressions: options,
56851                }))));
56852            };
56853            entries.push(entry);
56854        }
56855
56856        Ok(Some(Expression::OptionsProperty(Box::new(
56857            OptionsProperty { entries },
56858        ))))
56859    }
56860
56861    fn option_entry_from_expression(expr: &Expression) -> Option<OptionEntry> {
56862        let Expression::Eq(eq) = expr else {
56863            return None;
56864        };
56865
56866        let key = match &eq.left {
56867            Expression::Column(col) if col.table.is_none() => col.name.clone(),
56868            Expression::Identifier(id) => id.clone(),
56869            Expression::Var(var) => Identifier {
56870                name: var.this.clone(),
56871                quoted: false,
56872                trailing_comments: Vec::new(),
56873                span: None,
56874            },
56875            _ => return None,
56876        };
56877
56878        Some(OptionEntry {
56879            key,
56880            value: eq.right.clone(),
56881        })
56882    }
56883
56884    /// parse_environment_list - Parses Databricks ENVIRONMENT list: (dependencies = '...', environment_version = '...')
56885    /// Parses key=value assignments where values can be string literals
56886    pub fn parse_environment_list(&mut self) -> Result<Vec<Expression>> {
56887        // Expect opening paren
56888        if !self.match_token(TokenType::LParen) {
56889            return Ok(Vec::new());
56890        }
56891
56892        // Parse comma-separated key=value pairs
56893        let mut env_items = Vec::new();
56894        loop {
56895            // Check for empty ENVIRONMENT () or end of list
56896            if self.check(TokenType::RParen) {
56897                break;
56898            }
56899
56900            // Parse key=value using parse_assignment which handles EQ operations
56901            if let Some(opt) = self.parse_assignment()? {
56902                env_items.push(opt);
56903            } else {
56904                break;
56905            }
56906
56907            if !self.match_token(TokenType::Comma) {
56908                break;
56909            }
56910        }
56911
56912        // Expect closing paren
56913        self.expect(TokenType::RParen)?;
56914
56915        Ok(env_items)
56916    }
56917
56918    /// parse_wrapped_properties - Ported from Python _parse_wrapped_properties
56919    /// Parses properties wrapped in parentheses
56920    #[allow(unused_variables, unused_mut)]
56921    pub fn parse_wrapped_properties(&mut self) -> Result<Option<Expression>> {
56922        // Parse wrapped list of properties: (prop1, prop2, ...)
56923        if !self.match_token(TokenType::LParen) {
56924            return Ok(None);
56925        }
56926
56927        let mut props = Vec::new();
56928        loop {
56929            if let Some(prop) = self.parse_property()? {
56930                props.push(prop);
56931            }
56932            if !self.match_token(TokenType::Comma) {
56933                break;
56934            }
56935        }
56936
56937        self.match_token(TokenType::RParen);
56938
56939        if props.is_empty() {
56940            return Ok(None);
56941        }
56942
56943        // Return as a Properties expression
56944        Ok(Some(Expression::Properties(Box::new(Properties {
56945            expressions: props,
56946        }))))
56947    }
56948
56949    /// parse_wrapped_select - Ported from Python _parse_wrapped_select
56950    /// Parses wrapped select statements including PIVOT/UNPIVOT and FROM-first syntax
56951    #[allow(unused_variables, unused_mut)]
56952    pub fn parse_wrapped_select(&mut self, table: bool) -> Result<Option<Expression>> {
56953        // Check for PIVOT/UNPIVOT
56954        let is_unpivot = self.check(TokenType::Unpivot);
56955        if self.match_token(TokenType::Pivot) || self.match_token(TokenType::Unpivot) {
56956            // Call simplified pivot parser
56957            return self.parse_simplified_pivot(is_unpivot);
56958        }
56959
56960        // Check for FROM (DuckDB FROM-first syntax)
56961        if self.match_token(TokenType::From) {
56962            // Parse the FROM clause (table reference)
56963            let from_expr = self.parse_table()?;
56964
56965            // Try to parse a full SELECT
56966            let select = self.parse_select_query()?;
56967
56968            if let Some(sel) = select {
56969                // Apply set operations and query modifiers
56970                let with_ops = self.parse_set_operations_with_expr(Some(sel))?;
56971                return Ok(with_ops);
56972            } else if let Some(from_table) = from_expr {
56973                // Create a SELECT * FROM <table>
56974                let mut select_struct = Select::new();
56975                select_struct.expressions = vec![Expression::Star(Star {
56976                    table: None,
56977                    except: None,
56978                    replace: None,
56979                    rename: None,
56980                    trailing_comments: Vec::new(),
56981                    span: None,
56982                })];
56983                select_struct.from = Some(From {
56984                    expressions: vec![from_table],
56985                });
56986                let select_all = Expression::Select(Box::new(select_struct));
56987                let with_ops = self.parse_set_operations_with_expr(Some(select_all))?;
56988                return Ok(with_ops);
56989            }
56990            return Ok(None);
56991        }
56992
56993        // Regular case: parse table or nested select
56994        let this = if table {
56995            self.parse_table()?
56996        } else {
56997            // Parse nested select without set operations
56998            self.parse_select_query()?
56999        };
57000
57001        if this.is_none() {
57002            return Ok(None);
57003        }
57004
57005        // Apply set operations and query modifiers
57006        let with_ops = self.parse_set_operations_with_expr(this)?;
57007        Ok(with_ops)
57008    }
57009
57010    /// Helper for parse_wrapped_select with default table=false
57011    pub fn parse_wrapped_select_default(&mut self) -> Result<Option<Expression>> {
57012        self.parse_wrapped_select(false)
57013    }
57014
57015    /// parse_xml_element - Implemented from Python _parse_xml_element
57016    /// Python: parser.py:6917-6931
57017    /// Parses XMLELEMENT(NAME name [, expr, ...]) or XMLELEMENT(EVALNAME expr [, expr, ...])
57018    #[allow(unused_variables, unused_mut)]
57019    pub fn parse_xml_element(&mut self) -> Result<Option<Expression>> {
57020        let (this, evalname) = if self.match_text_seq(&["EVALNAME"]) {
57021            // EVALNAME - parse expression for dynamic element name
57022            let expr = self.parse_bitwise()?;
57023            (
57024                expr,
57025                Some(Box::new(Expression::Boolean(BooleanLiteral {
57026                    value: true,
57027                }))),
57028            )
57029        } else {
57030            // NAME - parse static element name
57031            self.match_text_seq(&["NAME"]);
57032            let id = self.parse_id_var()?;
57033            (id, None)
57034        };
57035
57036        // Parse optional expressions (comma-separated content/attributes)
57037        let expressions = if self.match_token(TokenType::Comma) {
57038            self.parse_expression_list()?
57039        } else {
57040            Vec::new()
57041        };
57042
57043        match this {
57044            Some(t) => Ok(Some(Expression::XMLElement(Box::new(XMLElement {
57045                this: Box::new(t),
57046                expressions,
57047                evalname,
57048            })))),
57049            None => Ok(None),
57050        }
57051    }
57052
57053    /// parse_xml_namespace - Ported from Python _parse_xml_namespace
57054    /// Parses XML namespace declarations
57055    #[allow(unused_variables, unused_mut)]
57056    pub fn parse_xml_namespace(&mut self) -> Result<Option<Expression>> {
57057        let mut namespaces = Vec::new();
57058
57059        loop {
57060            // Check for DEFAULT namespace
57061            let is_default = self.match_text_seq(&["DEFAULT"]);
57062
57063            // Parse the URI string
57064            let uri = if is_default {
57065                self.parse_string()?
57066            } else {
57067                // Parse URI with optional alias (AS name)
57068                let uri_expr = self.parse_string()?;
57069                if let Some(u) = uri_expr {
57070                    self.parse_alias_with_expr(Some(u))?
57071                } else {
57072                    None
57073                }
57074            };
57075
57076            if let Some(u) = uri {
57077                namespaces.push(u);
57078            }
57079
57080            // Continue if comma
57081            if !self.match_token(TokenType::Comma) {
57082                break;
57083            }
57084        }
57085
57086        if namespaces.is_empty() {
57087            return Ok(None);
57088        }
57089
57090        // Return as a Tuple (list of namespaces)
57091        Ok(Some(Expression::Tuple(Box::new(Tuple {
57092            expressions: namespaces,
57093        }))))
57094    }
57095
57096    /// parse_xml_table - Implemented from Python _parse_xml_table
57097    /// Python: parser.py:6933-6961
57098    /// Parses XMLTABLE(xpath_expr PASSING xml_doc COLUMNS ...)
57099    #[allow(unused_variables, unused_mut)]
57100    pub fn parse_xml_table(&mut self) -> Result<Option<Expression>> {
57101        // Parse optional XMLNAMESPACES clause
57102        let namespaces = if self.match_text_seq(&["XMLNAMESPACES", "("]) {
57103            let ns = self.parse_xml_namespace()?;
57104            self.match_text_seq(&[")", ","]);
57105            ns.map(Box::new)
57106        } else {
57107            None
57108        };
57109
57110        // Parse XPath expression (string)
57111        let this = self.parse_string()?;
57112        if this.is_none() {
57113            return Ok(None);
57114        }
57115
57116        // Parse PASSING clause
57117        let passing = if self.match_text_seq(&["PASSING"]) {
57118            // BY VALUE is optional
57119            self.match_text_seq(&["BY", "VALUE"]);
57120            // Parse comma-separated expressions.
57121            // Oracle XMLTABLE PASSING accepts full expressions (including function calls),
57122            // not just column references.
57123            // We need to stop before COLUMNS, RETURNING, or )
57124            let mut cols = Vec::new();
57125            loop {
57126                // Check for stop keywords before parsing a column
57127                if !self.is_at_end() {
57128                    let next_text = self.peek().text.to_ascii_uppercase();
57129                    if next_text == "COLUMNS" || next_text == "RETURNING" {
57130                        break;
57131                    }
57132                    if self.check(TokenType::RParen) {
57133                        break;
57134                    }
57135                }
57136                if let Some(col) = self.parse_assignment()? {
57137                    cols.push(col);
57138                } else {
57139                    break;
57140                }
57141                if !self.match_token(TokenType::Comma) {
57142                    break;
57143                }
57144            }
57145            if cols.is_empty() {
57146                None
57147            } else {
57148                Some(Box::new(Expression::Tuple(Box::new(Tuple {
57149                    expressions: cols,
57150                }))))
57151            }
57152        } else {
57153            None
57154        };
57155
57156        // Parse optional RETURNING SEQUENCE BY REF
57157        let by_ref = if self.match_text_seq(&["RETURNING", "SEQUENCE", "BY", "REF"]) {
57158            Some(Box::new(Expression::Boolean(BooleanLiteral {
57159                value: true,
57160            })))
57161        } else {
57162            None
57163        };
57164
57165        // Parse COLUMNS clause
57166        let columns = if self.match_text_seq(&["COLUMNS"]) {
57167            let mut cols = Vec::new();
57168            loop {
57169                // Stop if we hit the closing paren
57170                if self.check(TokenType::RParen) {
57171                    break;
57172                }
57173                // Be permissive with leading commas in multiline XMLTABLE COLUMNS lists.
57174                if self.match_token(TokenType::Comma) {
57175                    continue;
57176                }
57177                if let Some(col_def) = self.parse_field_def()? {
57178                    cols.push(col_def);
57179                } else {
57180                    break;
57181                }
57182                if !self.match_token(TokenType::Comma) {
57183                    break;
57184                }
57185            }
57186            cols
57187        } else {
57188            Vec::new()
57189        };
57190
57191        Ok(Some(Expression::XMLTable(Box::new(XMLTable {
57192            this: Box::new(this.unwrap()),
57193            namespaces,
57194            passing,
57195            columns,
57196            by_ref,
57197        }))))
57198    }
57199
57200    /// Parse UNLOAD statement (Athena/Presto/Redshift)
57201    /// UNLOAD (SELECT ...) TO 'location' WITH (options)
57202    fn parse_unload(&mut self) -> Result<Expression> {
57203        // Collect entire statement as a Command
57204        let mut parts = Vec::new();
57205        parts.push(self.advance().text.clone()); // consume UNLOAD
57206        parts.push(" ".to_string()); // space after UNLOAD
57207
57208        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
57209            let token_type = self.peek().token_type;
57210            let token_text = self.peek().text.clone();
57211
57212            // Track string literals
57213            if token_type == TokenType::String {
57214                parts.push(format!("'{}'", token_text.replace('\'', "''")));
57215                self.skip();
57216                // Add space after string unless followed by punctuation
57217                if !self.is_at_end() {
57218                    let next_type = self.peek().token_type;
57219                    if !matches!(
57220                        next_type,
57221                        TokenType::Comma | TokenType::RParen | TokenType::Semicolon
57222                    ) {
57223                        parts.push(" ".to_string());
57224                    }
57225                }
57226                continue;
57227            }
57228
57229            // Handle ARRAY[...] syntax - no space between ARRAY and [
57230            if token_text.eq_ignore_ascii_case("ARRAY")
57231                && self
57232                    .peek_nth(1)
57233                    .is_some_and(|t| t.token_type == TokenType::LBracket)
57234            {
57235                parts.push(token_text);
57236                self.skip();
57237                // Consume [
57238                parts.push("[".to_string());
57239                self.skip();
57240                // Collect until RBracket
57241                while !self.is_at_end() && !self.check(TokenType::RBracket) {
57242                    let inner_type = self.peek().token_type;
57243                    let inner_text = self.peek().text.clone();
57244                    if inner_type == TokenType::String {
57245                        parts.push(format!("'{}'", inner_text.replace('\'', "''")));
57246                    } else {
57247                        parts.push(inner_text);
57248                    }
57249                    self.skip();
57250                    if self.check(TokenType::Comma) {
57251                        parts.push(", ".to_string());
57252                        self.skip();
57253                    }
57254                }
57255                if self.check(TokenType::RBracket) {
57256                    parts.push("]".to_string());
57257                    self.skip();
57258                }
57259                continue;
57260            }
57261
57262            parts.push(token_text);
57263            self.skip();
57264
57265            // Add space after most tokens except punctuation
57266            if !self.is_at_end() {
57267                let next_type = self.peek().token_type;
57268                let no_space_before = matches!(
57269                    next_type,
57270                    TokenType::Comma
57271                        | TokenType::RParen
57272                        | TokenType::RBracket
57273                        | TokenType::Semicolon
57274                        | TokenType::LBracket
57275                );
57276                let no_space_after = matches!(token_type, TokenType::LParen | TokenType::LBracket);
57277                if !no_space_before && !no_space_after {
57278                    parts.push(" ".to_string());
57279                }
57280            }
57281        }
57282
57283        Ok(Expression::Command(Box::new(Command {
57284            this: parts.join(""),
57285        })))
57286    }
57287
57288    /// Parse USING EXTERNAL FUNCTION statement (Athena)
57289    /// USING EXTERNAL FUNCTION name(params) RETURNS type LAMBDA 'arn' SELECT ...
57290    fn parse_using_external_function(&mut self) -> Result<Expression> {
57291        // Record start position
57292        let start_pos = self.peek().span.start;
57293
57294        // Advance through all tokens until end or semicolon
57295        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
57296            self.skip();
57297        }
57298
57299        // Get end position from the last consumed token
57300        let end_pos = if self.current > 0 {
57301            self.tokens[self.current - 1].span.end
57302        } else {
57303            start_pos
57304        };
57305
57306        // Extract exact text from source if available
57307        let command_text = if let Some(ref source) = self.source {
57308            source[start_pos..end_pos].to_string()
57309        } else {
57310            // Fallback: reconstruct from tokens (loses whitespace)
57311            let mut parts = Vec::new();
57312            for i in 0..self.current {
57313                if self.tokens[i].span.start >= start_pos && self.tokens[i].span.end <= end_pos {
57314                    if self.tokens[i].token_type == TokenType::String {
57315                        parts.push(format!("'{}'", self.tokens[i].text.replace('\'', "''")));
57316                    } else {
57317                        parts.push(self.tokens[i].text.clone());
57318                    }
57319                    if i + 1 < self.current {
57320                        parts.push(" ".to_string());
57321                    }
57322                }
57323            }
57324            parts.join("")
57325        };
57326
57327        Ok(Expression::Command(Box::new(Command {
57328            this: command_text,
57329        })))
57330    }
57331}
57332
57333#[cfg(test)]
57334mod tests {
57335    use super::*;
57336    use crate::traversal::ExpressionWalk;
57337
57338    #[test]
57339    fn test_comment_before_limit() {
57340        let sql = "SELECT a FROM b WHERE foo AND bla\n-- comment 3\nLIMIT 10";
57341        let result = Parser::parse_sql(sql).unwrap();
57342        let output = crate::Generator::sql(&result[0]).unwrap();
57343        assert_eq!(
57344            output,
57345            "SELECT a FROM b WHERE foo AND bla LIMIT 10 /* comment 3 */"
57346        );
57347    }
57348
57349    #[test]
57350    fn test_variadic_array_postgres() {
57351        use crate::dialects::DialectType;
57352        use crate::transpile;
57353
57354        // Test: ARRAY[10, -1, 5, 4.4] should parse correctly in Postgres
57355        let sql = "SELECT ARRAY[10, -1, 5, 4.4]";
57356        let result = transpile(sql, DialectType::PostgreSQL, DialectType::PostgreSQL).unwrap();
57357        eprintln!("Array test: {} -> {}", sql, result[0]);
57358
57359        // Test: VARIADIC ARRAY[10, -1, 5, 4.4] in function call
57360        let sql2 = "SELECT MLEAST(VARIADIC ARRAY[10, -1, 5, 4.4])";
57361        let result2 = transpile(sql2, DialectType::PostgreSQL, DialectType::PostgreSQL).unwrap();
57362        eprintln!("VARIADIC test: {} -> {}", sql2, result2[0]);
57363        assert_eq!(result2[0], sql2);
57364    }
57365
57366    #[test]
57367    fn test_parse_simple_select() {
57368        let result = Parser::parse_sql("SELECT 1").unwrap();
57369        assert_eq!(result.len(), 1);
57370        assert!(result[0].is_select());
57371    }
57372
57373    #[test]
57374    fn test_parse_select_from() {
57375        let result = Parser::parse_sql("SELECT a, b FROM t").unwrap();
57376        assert_eq!(result.len(), 1);
57377
57378        let select = result[0].as_select().unwrap();
57379        assert_eq!(select.expressions.len(), 2);
57380        assert!(select.from.is_some());
57381    }
57382
57383    #[test]
57384    fn test_parse_select_where() {
57385        let result = Parser::parse_sql("SELECT * FROM t WHERE x = 1").unwrap();
57386        let select = result[0].as_select().unwrap();
57387        assert!(select.where_clause.is_some());
57388    }
57389
57390    #[test]
57391    fn test_parse_balances_large_and_chain_depth() {
57392        let mut sql = String::from("SELECT 1 WHERE c0 = 0");
57393        for i in 1..4096 {
57394            sql.push_str(&format!(" AND c{i} = {i}"));
57395        }
57396
57397        let result = Parser::parse_sql(&sql).unwrap();
57398        let select = result[0].as_select().unwrap();
57399        let where_clause = select.where_clause.as_ref().expect("WHERE clause missing");
57400        let depth = where_clause.this.tree_depth();
57401        assert!(
57402            depth < 128,
57403            "Expected balanced boolean tree depth, got {}",
57404            depth
57405        );
57406    }
57407
57408    #[test]
57409    fn test_parse_balances_large_or_chain_depth() {
57410        let mut sql = String::from("SELECT 1 WHERE c0 = 0");
57411        for i in 1..4096 {
57412            sql.push_str(&format!(" OR c{i} = {i}"));
57413        }
57414
57415        let result = Parser::parse_sql(&sql).unwrap();
57416        let select = result[0].as_select().unwrap();
57417        let where_clause = select.where_clause.as_ref().expect("WHERE clause missing");
57418        let depth = where_clause.this.tree_depth();
57419        assert!(
57420            depth < 128,
57421            "Expected balanced boolean tree depth, got {}",
57422            depth
57423        );
57424    }
57425
57426    #[test]
57427    fn test_parse_select_join() {
57428        let result = Parser::parse_sql("SELECT * FROM a JOIN b ON a.id = b.id").unwrap();
57429        let select = result[0].as_select().unwrap();
57430        assert_eq!(select.joins.len(), 1);
57431        assert_eq!(select.joins[0].kind, JoinKind::Inner);
57432    }
57433
57434    #[test]
57435    fn test_parse_expression_precedence() {
57436        let result = Parser::parse_sql("SELECT 1 + 2 * 3").unwrap();
57437        let select = result[0].as_select().unwrap();
57438        // Should parse as 1 + (2 * 3) due to precedence
57439        assert!(matches!(select.expressions[0], Expression::Add(_)));
57440    }
57441
57442    #[test]
57443    fn test_parse_function() {
57444        // COUNT(*) is now a typed Count expression
57445        let result = Parser::parse_sql("SELECT COUNT(*)").unwrap();
57446        let select = result[0].as_select().unwrap();
57447        assert!(matches!(select.expressions[0], Expression::Count(_)));
57448
57449        // Unknown functions stay as generic Function
57450        let result = Parser::parse_sql("SELECT MY_CUSTOM_FUNC(name)").unwrap();
57451        let select = result[0].as_select().unwrap();
57452        assert!(matches!(select.expressions[0], Expression::Function(_)));
57453
57454        // Known aggregate functions are now typed
57455        let result = Parser::parse_sql("SELECT SUM(amount)").unwrap();
57456        let select = result[0].as_select().unwrap();
57457        assert!(matches!(select.expressions[0], Expression::Sum(_)));
57458    }
57459
57460    #[test]
57461    fn test_parse_window_function() {
57462        let result =
57463            Parser::parse_sql("SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)")
57464                .unwrap();
57465        let select = result[0].as_select().unwrap();
57466        assert!(matches!(
57467            select.expressions[0],
57468            Expression::WindowFunction(_)
57469        ));
57470    }
57471
57472    #[test]
57473    fn test_parse_window_function_with_frame() {
57474        let result = Parser::parse_sql("SELECT SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)").unwrap();
57475        let select = result[0].as_select().unwrap();
57476        assert!(matches!(
57477            select.expressions[0],
57478            Expression::WindowFunction(_)
57479        ));
57480    }
57481
57482    #[test]
57483    fn test_parse_subscript() {
57484        // Array subscript
57485        let result = Parser::parse_sql("SELECT arr[0]").unwrap();
57486        let select = result[0].as_select().unwrap();
57487        assert!(matches!(select.expressions[0], Expression::Subscript(_)));
57488
57489        // Function result subscript
57490        let result = Parser::parse_sql("SELECT SPLIT(name, ',')[0]").unwrap();
57491        let select = result[0].as_select().unwrap();
57492        assert!(matches!(select.expressions[0], Expression::Subscript(_)));
57493    }
57494
57495    #[test]
57496    fn test_parse_case() {
57497        let result = Parser::parse_sql("SELECT CASE WHEN x = 1 THEN 'a' ELSE 'b' END").unwrap();
57498        let select = result[0].as_select().unwrap();
57499        assert!(matches!(select.expressions[0], Expression::Case(_)));
57500    }
57501
57502    #[test]
57503    fn test_parse_insert() {
57504        let result = Parser::parse_sql("INSERT INTO t (a, b) VALUES (1, 2)").unwrap();
57505        assert!(matches!(result[0], Expression::Insert(_)));
57506    }
57507
57508    #[test]
57509    fn test_parse_template_variable() {
57510        // Test Databricks/Hive ${variable} syntax
57511        let result = Parser::parse_sql("SELECT ${x} FROM ${y} WHERE ${z} > 1").unwrap();
57512        let select = result[0].as_select().unwrap();
57513        // The expression should be a Parameter with DollarBrace style
57514        assert!(
57515            matches!(&select.expressions[0], Expression::Parameter(p) if p.name == Some("x".to_string()))
57516        );
57517        // Check the style is DollarBrace
57518        if let Expression::Parameter(p) = &select.expressions[0] {
57519            assert_eq!(p.style, ParameterStyle::DollarBrace);
57520        }
57521    }
57522
57523    #[test]
57524    fn test_parse_update() {
57525        let result = Parser::parse_sql("UPDATE t SET a = 1 WHERE b = 2").unwrap();
57526        assert!(matches!(result[0], Expression::Update(_)));
57527    }
57528
57529    #[test]
57530    fn test_parse_delete() {
57531        let result = Parser::parse_sql("DELETE FROM t WHERE a = 1").unwrap();
57532        assert!(matches!(result[0], Expression::Delete(_)));
57533    }
57534
57535    // DDL tests
57536    #[test]
57537    fn test_parse_create_table() {
57538        let result = Parser::parse_sql(
57539            "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100) NOT NULL)",
57540        )
57541        .unwrap();
57542        assert!(matches!(result[0], Expression::CreateTable(_)));
57543
57544        if let Expression::CreateTable(ct) = &result[0] {
57545            assert_eq!(ct.name.name.name, "users");
57546            assert_eq!(ct.columns.len(), 2);
57547            assert!(ct.columns[0].primary_key);
57548            assert_eq!(ct.columns[1].nullable, Some(false));
57549        }
57550    }
57551
57552    #[test]
57553    fn test_parse_create_table_if_not_exists() {
57554        let result = Parser::parse_sql("CREATE TABLE IF NOT EXISTS t (id INT)").unwrap();
57555        if let Expression::CreateTable(ct) = &result[0] {
57556            assert!(ct.if_not_exists);
57557        }
57558    }
57559
57560    #[test]
57561    fn test_parse_create_temporary_table() {
57562        let result = Parser::parse_sql("CREATE TEMPORARY TABLE t (id INT)").unwrap();
57563        if let Expression::CreateTable(ct) = &result[0] {
57564            assert!(ct.temporary);
57565        }
57566    }
57567
57568    #[test]
57569    fn test_bigquery_create_table_properties_are_typed() {
57570        use crate::DialectType;
57571
57572        let sql = "CREATE OR REPLACE TABLE `p1`.`d1`.`t1` PARTITION BY DATE_TRUNC(day, month) CLUSTER BY some_cluster_column OPTIONS(description='', labels=[('l1', 'v1'), ('l2', 'v2')]) AS SELECT CURRENT_DATE AS day, DATE_TRUNC(CURRENT_DATE(), month) AS month, 'c' AS some_cluster_column";
57573        let parsed = crate::parse(sql, DialectType::BigQuery).unwrap();
57574
57575        let create = match &parsed[0] {
57576            Expression::CreateTable(ct) => ct,
57577            other => panic!(
57578                "Expected CreateTable, got {:?}",
57579                std::mem::discriminant(other)
57580            ),
57581        };
57582
57583        assert!(
57584            create
57585                .properties
57586                .iter()
57587                .any(|p| matches!(p, Expression::PartitionByProperty(_))),
57588            "Expected typed PARTITION BY property"
57589        );
57590        assert!(
57591            create
57592                .properties
57593                .iter()
57594                .any(|p| matches!(p, Expression::ClusterByColumnsProperty(_))),
57595            "Expected typed CLUSTER BY property"
57596        );
57597        assert!(
57598            create
57599                .properties
57600                .iter()
57601                .any(|p| matches!(p, Expression::OptionsProperty(_))),
57602            "Expected typed OPTIONS property"
57603        );
57604        assert!(
57605            !create
57606                .properties
57607                .iter()
57608                .any(|p| matches!(p, Expression::Raw(_))),
57609            "BigQuery table properties should not fall back to Raw"
57610        );
57611
57612        let options = create
57613            .properties
57614            .iter()
57615            .find_map(|p| match p {
57616                Expression::OptionsProperty(o) => Some(o),
57617                _ => None,
57618            })
57619            .expect("Expected OptionsProperty");
57620        assert_eq!(options.entries.len(), 2);
57621        assert_eq!(options.entries[0].key.name, "description");
57622        assert_eq!(options.entries[1].key.name, "labels");
57623    }
57624
57625    #[test]
57626    fn test_bigquery_create_table_properties_roundtrip() {
57627        use crate::DialectType;
57628
57629        let sql = "CREATE TABLE t1 PARTITION BY DATE_TRUNC(day, month) CLUSTER BY some_cluster_column OPTIONS(description='', labels=[('l1', 'v1')]) AS SELECT 1 AS day, 1 AS month, 'c' AS some_cluster_column";
57630        let expected = "CREATE TABLE t1 PARTITION BY DATE_TRUNC(day, month) CLUSTER BY some_cluster_column OPTIONS (description='', labels=[('l1', 'v1')]) AS SELECT 1 AS day, 1 AS month, 'c' AS some_cluster_column";
57631        let parsed = crate::parse(sql, DialectType::BigQuery).unwrap();
57632        let generated = crate::generate(&parsed[0], DialectType::BigQuery).unwrap();
57633        assert_eq!(generated, expected);
57634    }
57635
57636    #[test]
57637    fn test_parse_drop_table() {
57638        let result = Parser::parse_sql("DROP TABLE IF EXISTS users CASCADE").unwrap();
57639        assert!(matches!(result[0], Expression::DropTable(_)));
57640
57641        if let Expression::DropTable(dt) = &result[0] {
57642            assert!(dt.if_exists);
57643            assert!(dt.cascade);
57644            assert_eq!(dt.names.len(), 1);
57645        }
57646    }
57647
57648    #[test]
57649    fn test_parse_alter_table_add_column() {
57650        let result = Parser::parse_sql("ALTER TABLE users ADD COLUMN email VARCHAR(255)").unwrap();
57651        assert!(matches!(result[0], Expression::AlterTable(_)));
57652
57653        if let Expression::AlterTable(at) = &result[0] {
57654            assert_eq!(at.actions.len(), 1);
57655            assert!(matches!(at.actions[0], AlterTableAction::AddColumn { .. }));
57656        }
57657    }
57658
57659    #[test]
57660    fn test_parse_alter_table_drop_column() {
57661        let result = Parser::parse_sql("ALTER TABLE users DROP COLUMN email").unwrap();
57662        if let Expression::AlterTable(at) = &result[0] {
57663            assert!(matches!(at.actions[0], AlterTableAction::DropColumn { .. }));
57664        }
57665    }
57666
57667    #[test]
57668    fn test_tsql_alter_table_set_options() {
57669        use crate::{transpile, DialectType};
57670        let tests = vec![
57671            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=OFF)",
57672            "ALTER TABLE tbl SET (FILESTREAM_ON = 'test')",
57673            "ALTER TABLE tbl SET (DATA_DELETION=ON)",
57674            "ALTER TABLE tbl SET (DATA_DELETION=OFF)",
57675            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=ON(HISTORY_TABLE=db.tbl, DATA_CONSISTENCY_CHECK=OFF, HISTORY_RETENTION_PERIOD=5 DAYS))",
57676            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=ON(HISTORY_TABLE=db.tbl, HISTORY_RETENTION_PERIOD=INFINITE))",
57677            "ALTER TABLE tbl SET (DATA_DELETION=ON(FILTER_COLUMN=col, RETENTION_PERIOD=5 MONTHS))",
57678        ];
57679        for sql in tests {
57680            let result = transpile(sql, DialectType::TSQL, DialectType::TSQL);
57681            match result {
57682                Ok(output) => {
57683                    assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
57684                }
57685                Err(e) => {
57686                    panic!("Parse/generate failed for: {} -- {:?}", sql, e);
57687                }
57688            }
57689        }
57690    }
57691
57692    #[test]
57693    fn test_parse_create_index() {
57694        let result = Parser::parse_sql("CREATE UNIQUE INDEX idx_email ON users (email)").unwrap();
57695        assert!(matches!(result[0], Expression::CreateIndex(_)));
57696
57697        if let Expression::CreateIndex(ci) = &result[0] {
57698            assert!(ci.unique);
57699            assert_eq!(ci.name.name, "idx_email");
57700            assert_eq!(ci.table.name.name, "users");
57701            assert_eq!(ci.columns.len(), 1);
57702        }
57703    }
57704
57705    #[test]
57706    fn test_parse_drop_index() {
57707        let result = Parser::parse_sql("DROP INDEX IF EXISTS idx_email ON users").unwrap();
57708        assert!(matches!(result[0], Expression::DropIndex(_)));
57709
57710        if let Expression::DropIndex(di) = &result[0] {
57711            assert!(di.if_exists);
57712            assert!(di.table.is_some());
57713        }
57714    }
57715
57716    #[test]
57717    fn test_parse_create_view() {
57718        let result =
57719            Parser::parse_sql("CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1")
57720                .unwrap();
57721        assert!(matches!(result[0], Expression::CreateView(_)));
57722    }
57723
57724    #[test]
57725    fn test_parse_create_materialized_view() {
57726        let result =
57727            Parser::parse_sql("CREATE MATERIALIZED VIEW stats AS SELECT COUNT(*) FROM users")
57728                .unwrap();
57729        if let Expression::CreateView(cv) = &result[0] {
57730            assert!(cv.materialized);
57731        }
57732    }
57733
57734    #[test]
57735    fn test_hive_stored_by() {
57736        use crate::{transpile, DialectType};
57737        let sql = "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'";
57738        let result = transpile(sql, DialectType::Hive, DialectType::Hive);
57739        match result {
57740            Ok(output) => {
57741                assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
57742            }
57743            Err(e) => {
57744                panic!("Parse/generate failed for: {} -- {:?}", sql, e);
57745            }
57746        }
57747    }
57748
57749    #[test]
57750    fn test_hive_row_format_serde() {
57751        use crate::{transpile, DialectType};
57752
57753        // Test various Hive CREATE TABLE syntax
57754        let test_cases = vec![
57755            (
57756                "CREATE TABLE my_table (a7 ARRAY<DATE>)",
57757                "CREATE TABLE my_table (a7 ARRAY<DATE>)",
57758            ),
57759            (
57760                "CREATE EXTERNAL TABLE my_table (x INT) ROW FORMAT SERDE 'a'",
57761                "CREATE EXTERNAL TABLE my_table (x INT) ROW FORMAT SERDE 'a'",
57762            ),
57763            (
57764                "CREATE EXTERNAL TABLE my_table (x INT) STORED AS INPUTFORMAT 'b' OUTPUTFORMAT 'c'",
57765                "CREATE EXTERNAL TABLE my_table (x INT) STORED AS INPUTFORMAT 'b' OUTPUTFORMAT 'c'",
57766            ),
57767            (
57768                "CREATE EXTERNAL TABLE my_table (x INT) LOCATION 'd'",
57769                "CREATE EXTERNAL TABLE my_table (x INT) LOCATION 'd'",
57770            ),
57771            (
57772                "CREATE EXTERNAL TABLE my_table (x INT) TBLPROPERTIES ('e'='f')",
57773                "CREATE EXTERNAL TABLE my_table (x INT) TBLPROPERTIES ('e'='f')",
57774            ),
57775            (
57776                "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'",
57777                "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'",
57778            ),
57779        ];
57780
57781        for (sql, expected) in &test_cases {
57782            let result = transpile(sql, DialectType::Hive, DialectType::Hive);
57783            match result {
57784                Ok(output) => {
57785                    assert_eq!(output[0].trim(), *expected, "Identity failed for: {}", sql);
57786                }
57787                Err(e) => {
57788                    panic!("Parse/generate failed for: {} -- {:?}", sql, e);
57789                }
57790            }
57791        }
57792
57793        // Test full case with all Hive table properties
57794        let sql = "CREATE EXTERNAL TABLE `my_table` (`a7` ARRAY<DATE>) ROW FORMAT SERDE 'a' STORED AS INPUTFORMAT 'b' OUTPUTFORMAT 'c' LOCATION 'd' TBLPROPERTIES ('e'='f')";
57795        let result = transpile(sql, DialectType::Hive, DialectType::Hive);
57796        match result {
57797            Ok(output) => {
57798                assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
57799            }
57800            Err(e) => {
57801                panic!("Parse/generate failed for: {} -- {:?}", sql, e);
57802            }
57803        }
57804    }
57805
57806    #[test]
57807    fn test_parse_drop_view() {
57808        let result = Parser::parse_sql("DROP VIEW IF EXISTS active_users").unwrap();
57809        assert!(matches!(result[0], Expression::DropView(_)));
57810    }
57811
57812    #[test]
57813    fn test_parse_truncate() {
57814        let result = Parser::parse_sql("TRUNCATE TABLE users CASCADE").unwrap();
57815        assert!(matches!(result[0], Expression::Truncate(_)));
57816
57817        if let Expression::Truncate(tr) = &result[0] {
57818            assert!(tr.cascade);
57819        }
57820    }
57821
57822    // Tests for typed aggregate functions
57823    #[test]
57824    fn test_parse_typed_aggregates() {
57825        // COUNT with DISTINCT
57826        let result = Parser::parse_sql("SELECT COUNT(DISTINCT user_id)").unwrap();
57827        let select = result[0].as_select().unwrap();
57828        if let Expression::Count(c) = &select.expressions[0] {
57829            assert!(c.distinct);
57830            assert!(!c.star);
57831        } else {
57832            panic!("Expected Count expression");
57833        }
57834
57835        // AVG
57836        let result = Parser::parse_sql("SELECT AVG(price)").unwrap();
57837        let select = result[0].as_select().unwrap();
57838        assert!(matches!(select.expressions[0], Expression::Avg(_)));
57839
57840        // MIN/MAX
57841        let result = Parser::parse_sql("SELECT MIN(a), MAX(b)").unwrap();
57842        let select = result[0].as_select().unwrap();
57843        assert!(matches!(select.expressions[0], Expression::Min(_)));
57844        assert!(matches!(select.expressions[1], Expression::Max(_)));
57845
57846        // STDDEV/VARIANCE
57847        let result = Parser::parse_sql("SELECT STDDEV(x), VARIANCE(y)").unwrap();
57848        let select = result[0].as_select().unwrap();
57849        assert!(matches!(select.expressions[0], Expression::Stddev(_)));
57850        assert!(matches!(select.expressions[1], Expression::Variance(_)));
57851    }
57852
57853    #[test]
57854    fn test_parse_typed_window_functions() {
57855        // ROW_NUMBER
57856        let result = Parser::parse_sql("SELECT ROW_NUMBER() OVER (ORDER BY id)").unwrap();
57857        let select = result[0].as_select().unwrap();
57858        if let Expression::WindowFunction(wf) = &select.expressions[0] {
57859            assert!(matches!(wf.this, Expression::RowNumber(_)));
57860        } else {
57861            panic!("Expected WindowFunction");
57862        }
57863
57864        // RANK and DENSE_RANK
57865        let result = Parser::parse_sql("SELECT RANK() OVER (), DENSE_RANK() OVER ()").unwrap();
57866        let select = result[0].as_select().unwrap();
57867        if let Expression::WindowFunction(wf) = &select.expressions[0] {
57868            assert!(matches!(wf.this, Expression::Rank(_)));
57869        }
57870        if let Expression::WindowFunction(wf) = &select.expressions[1] {
57871            assert!(matches!(wf.this, Expression::DenseRank(_)));
57872        }
57873
57874        // LEAD/LAG
57875        let result = Parser::parse_sql("SELECT LEAD(val, 1, 0) OVER (ORDER BY id)").unwrap();
57876        let select = result[0].as_select().unwrap();
57877        if let Expression::WindowFunction(wf) = &select.expressions[0] {
57878            if let Expression::Lead(f) = &wf.this {
57879                assert!(f.offset.is_some());
57880                assert!(f.default.is_some());
57881            } else {
57882                panic!("Expected Lead");
57883            }
57884        }
57885
57886        // NTILE
57887        let result = Parser::parse_sql("SELECT NTILE(4) OVER (ORDER BY score)").unwrap();
57888        let select = result[0].as_select().unwrap();
57889        if let Expression::WindowFunction(wf) = &select.expressions[0] {
57890            assert!(matches!(wf.this, Expression::NTile(_)));
57891        }
57892    }
57893
57894    #[test]
57895    fn test_parse_string_functions() {
57896        // CONTAINS, STARTS_WITH, ENDS_WITH
57897        let result = Parser::parse_sql("SELECT CONTAINS(name, 'test')").unwrap();
57898        let select = result[0].as_select().unwrap();
57899        assert!(matches!(select.expressions[0], Expression::Contains(_)));
57900
57901        let result = Parser::parse_sql("SELECT STARTS_WITH(name, 'A')").unwrap();
57902        let select = result[0].as_select().unwrap();
57903        assert!(matches!(select.expressions[0], Expression::StartsWith(_)));
57904
57905        let result = Parser::parse_sql("SELECT ENDS_WITH(name, 'z')").unwrap();
57906        let select = result[0].as_select().unwrap();
57907        assert!(matches!(select.expressions[0], Expression::EndsWith(_)));
57908    }
57909
57910    #[test]
57911    fn test_parse_math_functions() {
57912        // MOD function
57913        let result = Parser::parse_sql("SELECT MOD(10, 3)").unwrap();
57914        let select = result[0].as_select().unwrap();
57915        assert!(matches!(select.expressions[0], Expression::ModFunc(_)));
57916
57917        // RANDOM and RAND
57918        let result = Parser::parse_sql("SELECT RANDOM()").unwrap();
57919        let select = result[0].as_select().unwrap();
57920        assert!(matches!(select.expressions[0], Expression::Random(_)));
57921
57922        let result = Parser::parse_sql("SELECT RAND(42)").unwrap();
57923        let select = result[0].as_select().unwrap();
57924        assert!(matches!(select.expressions[0], Expression::Rand(_)));
57925
57926        // Trigonometric functions
57927        let result = Parser::parse_sql("SELECT SIN(x), COS(x), TAN(x)").unwrap();
57928        let select = result[0].as_select().unwrap();
57929        assert!(matches!(select.expressions[0], Expression::Sin(_)));
57930        assert!(matches!(select.expressions[1], Expression::Cos(_)));
57931        assert!(matches!(select.expressions[2], Expression::Tan(_)));
57932    }
57933
57934    #[test]
57935    fn test_parse_date_functions() {
57936        // Date part extraction functions
57937        let result =
57938            Parser::parse_sql("SELECT YEAR(date_col), MONTH(date_col), DAY(date_col)").unwrap();
57939        let select = result[0].as_select().unwrap();
57940        assert!(matches!(select.expressions[0], Expression::Year(_)));
57941        assert!(matches!(select.expressions[1], Expression::Month(_)));
57942        assert!(matches!(select.expressions[2], Expression::Day(_)));
57943
57944        // EPOCH and EPOCH_MS
57945        let result = Parser::parse_sql("SELECT EPOCH(ts), EPOCH_MS(ts)").unwrap();
57946        let select = result[0].as_select().unwrap();
57947        assert!(matches!(select.expressions[0], Expression::Epoch(_)));
57948        assert!(matches!(select.expressions[1], Expression::EpochMs(_)));
57949    }
57950
57951    #[test]
57952    fn test_parse_array_functions() {
57953        // ARRAY_LENGTH
57954        let result = Parser::parse_sql("SELECT ARRAY_LENGTH(arr)").unwrap();
57955        let select = result[0].as_select().unwrap();
57956        assert!(matches!(select.expressions[0], Expression::ArrayLength(_)));
57957
57958        // ARRAY_CONTAINS
57959        let result = Parser::parse_sql("SELECT ARRAY_CONTAINS(arr, 1)").unwrap();
57960        let select = result[0].as_select().unwrap();
57961        assert!(matches!(
57962            select.expressions[0],
57963            Expression::ArrayContains(_)
57964        ));
57965
57966        // EXPLODE
57967        let result = Parser::parse_sql("SELECT EXPLODE(arr)").unwrap();
57968        let select = result[0].as_select().unwrap();
57969        assert!(matches!(select.expressions[0], Expression::Explode(_)));
57970    }
57971
57972    #[test]
57973    fn test_parse_json_functions() {
57974        // JSON_EXTRACT
57975        let result = Parser::parse_sql("SELECT JSON_EXTRACT(data, '$.name')").unwrap();
57976        let select = result[0].as_select().unwrap();
57977        assert!(matches!(select.expressions[0], Expression::JsonExtract(_)));
57978
57979        // JSON_ARRAY_LENGTH
57980        let result = Parser::parse_sql("SELECT JSON_ARRAY_LENGTH(arr)").unwrap();
57981        let select = result[0].as_select().unwrap();
57982        assert!(matches!(
57983            select.expressions[0],
57984            Expression::JsonArrayLength(_)
57985        ));
57986
57987        // TO_JSON and PARSE_JSON
57988        let result = Parser::parse_sql("SELECT TO_JSON(obj), PARSE_JSON(str)").unwrap();
57989        let select = result[0].as_select().unwrap();
57990        assert!(matches!(select.expressions[0], Expression::ToJson(_)));
57991        assert!(matches!(select.expressions[1], Expression::ParseJson(_)));
57992
57993        // JSON literal: JSON '"foo"' -> ParseJson
57994        let result = Parser::parse_sql("SELECT JSON '\"foo\"'").unwrap();
57995        let select = result[0].as_select().unwrap();
57996        assert!(
57997            matches!(select.expressions[0], Expression::ParseJson(_)),
57998            "Expected ParseJson, got: {:?}",
57999            select.expressions[0]
58000        );
58001    }
58002
58003    #[test]
58004    fn test_parse_map_functions() {
58005        // MAP_KEYS and MAP_VALUES
58006        let result = Parser::parse_sql("SELECT MAP_KEYS(m), MAP_VALUES(m)").unwrap();
58007        let select = result[0].as_select().unwrap();
58008        assert!(matches!(select.expressions[0], Expression::MapKeys(_)));
58009        assert!(matches!(select.expressions[1], Expression::MapValues(_)));
58010
58011        // ELEMENT_AT
58012        let result = Parser::parse_sql("SELECT ELEMENT_AT(m, 'key')").unwrap();
58013        let select = result[0].as_select().unwrap();
58014        assert!(matches!(select.expressions[0], Expression::ElementAt(_)));
58015    }
58016
58017    #[test]
58018    fn test_parse_date_literals() {
58019        // DATE literal (generic mode normalizes to CAST)
58020        let result = Parser::parse_sql("SELECT DATE '2024-01-15'").unwrap();
58021        let select = result[0].as_select().unwrap();
58022        match &select.expressions[0] {
58023            Expression::Cast(cast) => {
58024                match &cast.this {
58025                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
58026                        let Literal::String(s) = lit.as_ref() else {
58027                            unreachable!()
58028                        };
58029                        assert_eq!(s, "2024-01-15")
58030                    }
58031                    other => panic!("Expected String literal in Cast, got {:?}", other),
58032                }
58033                assert!(matches!(cast.to, DataType::Date));
58034            }
58035            other => panic!("Expected Cast expression, got {:?}", other),
58036        }
58037
58038        // TIME literal
58039        let result = Parser::parse_sql("SELECT TIME '10:30:00'").unwrap();
58040        let select = result[0].as_select().unwrap();
58041        match &select.expressions[0] {
58042            Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Time(_)) => {
58043                let Literal::Time(t) = lit.as_ref() else {
58044                    unreachable!()
58045                };
58046                assert_eq!(t, "10:30:00");
58047            }
58048            _ => panic!("Expected Time literal"),
58049        }
58050
58051        // TIMESTAMP literal -> CAST in generic mode
58052        let result = Parser::parse_sql("SELECT TIMESTAMP '2024-01-15 10:30:00'").unwrap();
58053        let select = result[0].as_select().unwrap();
58054        match &select.expressions[0] {
58055            Expression::Cast(cast) => {
58056                match &cast.this {
58057                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
58058                        let Literal::String(s) = lit.as_ref() else {
58059                            unreachable!()
58060                        };
58061                        assert_eq!(s, "2024-01-15 10:30:00")
58062                    }
58063                    other => panic!("Expected String literal inside Cast, got {:?}", other),
58064                }
58065                assert!(matches!(
58066                    &cast.to,
58067                    DataType::Timestamp {
58068                        precision: None,
58069                        timezone: false
58070                    }
58071                ));
58072            }
58073            _ => panic!("Expected Cast expression for TIMESTAMP literal"),
58074        }
58075    }
58076
58077    #[test]
58078    fn test_parse_star_exclude() {
58079        // EXCLUDE with multiple columns
58080        let result = Parser::parse_sql("SELECT * EXCLUDE (col1, col2) FROM t").unwrap();
58081        let select = result[0].as_select().unwrap();
58082        if let Expression::Star(star) = &select.expressions[0] {
58083            assert!(star.except.is_some());
58084            let except = star.except.as_ref().unwrap();
58085            assert_eq!(except.len(), 2);
58086            assert_eq!(except[0].name, "col1");
58087            assert_eq!(except[1].name, "col2");
58088        } else {
58089            panic!("Expected Star expression");
58090        }
58091
58092        // EXCEPT (BigQuery syntax)
58093        let result = Parser::parse_sql("SELECT * EXCEPT (id, created_at) FROM t").unwrap();
58094        let select = result[0].as_select().unwrap();
58095        if let Expression::Star(star) = &select.expressions[0] {
58096            assert!(star.except.is_some());
58097        } else {
58098            panic!("Expected Star expression");
58099        }
58100
58101        // table.* with EXCLUDE
58102        let result = Parser::parse_sql("SELECT t.* EXCLUDE (col1) FROM t").unwrap();
58103        let select = result[0].as_select().unwrap();
58104        if let Expression::Star(star) = &select.expressions[0] {
58105            assert!(star.table.is_some());
58106            assert_eq!(star.table.as_ref().unwrap().name, "t");
58107            assert!(star.except.is_some());
58108        } else {
58109            panic!("Expected Star expression");
58110        }
58111    }
58112
58113    #[test]
58114    fn test_parse_star_replace() {
58115        // REPLACE with single expression
58116        let result = Parser::parse_sql("SELECT * REPLACE (UPPER(name) AS name) FROM t").unwrap();
58117        let select = result[0].as_select().unwrap();
58118        if let Expression::Star(star) = &select.expressions[0] {
58119            assert!(star.replace.is_some());
58120            let replace = star.replace.as_ref().unwrap();
58121            assert_eq!(replace.len(), 1);
58122            assert_eq!(replace[0].alias.name, "name");
58123        } else {
58124            panic!("Expected Star expression");
58125        }
58126
58127        // REPLACE with multiple expressions
58128        let result = Parser::parse_sql("SELECT * REPLACE (a + 1 AS a, b * 2 AS b) FROM t").unwrap();
58129        let select = result[0].as_select().unwrap();
58130        if let Expression::Star(star) = &select.expressions[0] {
58131            let replace = star.replace.as_ref().unwrap();
58132            assert_eq!(replace.len(), 2);
58133        } else {
58134            panic!("Expected Star expression");
58135        }
58136    }
58137
58138    #[test]
58139    fn test_parse_star_rename() {
58140        // RENAME with multiple columns
58141        let result =
58142            Parser::parse_sql("SELECT * RENAME (old_col AS new_col, x AS y) FROM t").unwrap();
58143        let select = result[0].as_select().unwrap();
58144        if let Expression::Star(star) = &select.expressions[0] {
58145            assert!(star.rename.is_some());
58146            let rename = star.rename.as_ref().unwrap();
58147            assert_eq!(rename.len(), 2);
58148            assert_eq!(rename[0].0.name, "old_col");
58149            assert_eq!(rename[0].1.name, "new_col");
58150        } else {
58151            panic!("Expected Star expression");
58152        }
58153    }
58154
58155    #[test]
58156    fn test_parse_star_combined() {
58157        // EXCLUDE + REPLACE combined
58158        let result =
58159            Parser::parse_sql("SELECT * EXCLUDE (id) REPLACE (name || '!' AS name) FROM t")
58160                .unwrap();
58161        let select = result[0].as_select().unwrap();
58162        if let Expression::Star(star) = &select.expressions[0] {
58163            assert!(star.except.is_some());
58164            assert!(star.replace.is_some());
58165        } else {
58166            panic!("Expected Star expression");
58167        }
58168    }
58169
58170    #[test]
58171    fn test_parse_spatial_types() {
58172        // GEOMETRY with subtype and SRID (PostgreSQL syntax)
58173        let result = Parser::parse_sql("CREATE TABLE t (geom GEOMETRY(Point, 4326))").unwrap();
58174        if let Expression::CreateTable(ct) = &result[0] {
58175            assert_eq!(ct.columns.len(), 1);
58176            match &ct.columns[0].data_type {
58177                DataType::Geometry { subtype, srid } => {
58178                    assert_eq!(subtype.as_deref(), Some("POINT"));
58179                    assert_eq!(*srid, Some(4326));
58180                }
58181                _ => panic!("Expected Geometry type"),
58182            }
58183        }
58184
58185        // GEOGRAPHY without parameters
58186        let result = Parser::parse_sql("CREATE TABLE t (loc GEOGRAPHY)").unwrap();
58187        if let Expression::CreateTable(ct) = &result[0] {
58188            match &ct.columns[0].data_type {
58189                DataType::Geography { subtype, srid } => {
58190                    assert!(subtype.is_none());
58191                    assert!(srid.is_none());
58192                }
58193                _ => panic!("Expected Geography type"),
58194            }
58195        }
58196
58197        // GEOMETRY subtype only (no SRID)
58198        let result = Parser::parse_sql("CREATE TABLE t (geom GEOMETRY(LineString))").unwrap();
58199        if let Expression::CreateTable(ct) = &result[0] {
58200            match &ct.columns[0].data_type {
58201                DataType::Geometry { subtype, srid } => {
58202                    assert_eq!(subtype.as_deref(), Some("LINESTRING"));
58203                    assert!(srid.is_none());
58204                }
58205                _ => panic!("Expected Geometry type"),
58206            }
58207        }
58208
58209        // Simple POINT type (MySQL-style without SRID)
58210        let result = Parser::parse_sql("CREATE TABLE t (pt POINT)").unwrap();
58211        if let Expression::CreateTable(ct) = &result[0] {
58212            match &ct.columns[0].data_type {
58213                DataType::Geometry { subtype, srid } => {
58214                    assert_eq!(subtype.as_deref(), Some("POINT"));
58215                    assert!(srid.is_none());
58216                }
58217                _ => panic!("Expected Geometry type"),
58218            }
58219        }
58220    }
58221
58222    #[test]
58223    fn test_parse_duckdb_pivot_simple() {
58224        let sql = "PIVOT Cities ON Year USING SUM(Population)";
58225        let result = Parser::parse_sql(sql);
58226        assert!(
58227            result.is_ok(),
58228            "Failed to parse: {} - {:?}",
58229            sql,
58230            result.err()
58231        );
58232        let stmts = result.unwrap();
58233        assert_eq!(
58234            stmts.len(),
58235            1,
58236            "Expected 1 statement, got {}: {:?}",
58237            stmts.len(),
58238            stmts
58239        );
58240        match &stmts[0] {
58241            Expression::Pivot(p) => {
58242                assert!(!p.unpivot);
58243                assert!(!p.expressions.is_empty(), "Should have ON expressions");
58244                assert!(!p.using.is_empty(), "Should have USING expressions");
58245            }
58246            other => panic!("Expected Pivot, got {:?}", other),
58247        }
58248    }
58249
58250    #[test]
58251    fn test_parse_duckdb_pivot_with_group_by() {
58252        let sql = "PIVOT Cities ON Year USING SUM(Population) GROUP BY Country";
58253        let result = Parser::parse_sql(sql);
58254        assert!(
58255            result.is_ok(),
58256            "Failed to parse: {} - {:?}",
58257            sql,
58258            result.err()
58259        );
58260    }
58261
58262    #[test]
58263    fn test_parse_duckdb_unpivot() {
58264        let sql = "UNPIVOT monthly_sales ON jan, feb, mar INTO NAME month VALUE sales";
58265        let result = Parser::parse_sql(sql);
58266        assert!(
58267            result.is_ok(),
58268            "Failed to parse: {} - {:?}",
58269            sql,
58270            result.err()
58271        );
58272    }
58273
58274    #[test]
58275    fn test_parse_standard_pivot_in_from() {
58276        let sql = "SELECT * FROM cities PIVOT(SUM(population) FOR year IN (2000, 2010, 2020))";
58277        let result = Parser::parse_sql(sql);
58278        assert!(
58279            result.is_ok(),
58280            "Failed to parse: {} - {:?}",
58281            sql,
58282            result.err()
58283        );
58284    }
58285
58286    fn assert_pivot_roundtrip(sql: &str) {
58287        let parsed = crate::parse(sql, crate::DialectType::DuckDB);
58288        assert!(
58289            parsed.is_ok(),
58290            "Failed to parse: {} - {:?}",
58291            sql,
58292            parsed.err()
58293        );
58294        let stmts = parsed.unwrap();
58295        assert_eq!(stmts.len(), 1, "Expected 1 statement for: {}", sql);
58296        let generated = crate::generate(&stmts[0], crate::DialectType::DuckDB);
58297        assert!(
58298            generated.is_ok(),
58299            "Failed to generate: {} - {:?}",
58300            sql,
58301            generated.err()
58302        );
58303        let result = generated.unwrap();
58304        assert_eq!(result.trim(), sql, "Round-trip mismatch for: {}", sql);
58305    }
58306
58307    fn assert_pivot_roundtrip_bq(sql: &str) {
58308        let parsed = crate::parse(sql, crate::DialectType::BigQuery);
58309        assert!(
58310            parsed.is_ok(),
58311            "Failed to parse: {} - {:?}",
58312            sql,
58313            parsed.err()
58314        );
58315        let stmts = parsed.unwrap();
58316        assert_eq!(stmts.len(), 1, "Expected 1 statement for: {}", sql);
58317        let generated = crate::generate(&stmts[0], crate::DialectType::BigQuery);
58318        assert!(
58319            generated.is_ok(),
58320            "Failed to generate: {} - {:?}",
58321            sql,
58322            generated.err()
58323        );
58324        let result = generated.unwrap();
58325        assert_eq!(result.trim(), sql, "Round-trip mismatch for: {}", sql);
58326    }
58327
58328    #[test]
58329    fn test_pivot_roundtrip_duckdb_simple() {
58330        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population)");
58331    }
58332
58333    #[test]
58334    fn test_pivot_roundtrip_duckdb_group_by() {
58335        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) GROUP BY Country");
58336    }
58337
58338    #[test]
58339    fn test_pivot_roundtrip_duckdb_in_clause() {
58340        assert_pivot_roundtrip(
58341            "PIVOT Cities ON Year IN (2000, 2010) USING SUM(Population) GROUP BY Country",
58342        );
58343    }
58344
58345    #[test]
58346    fn test_pivot_roundtrip_duckdb_multiple_using() {
58347        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) AS total, MAX(Population) AS max GROUP BY Country");
58348    }
58349
58350    #[test]
58351    fn test_pivot_roundtrip_duckdb_multiple_on() {
58352        assert_pivot_roundtrip("PIVOT Cities ON Country, Name USING SUM(Population)");
58353    }
58354
58355    #[test]
58356    fn test_pivot_roundtrip_duckdb_concat_on() {
58357        assert_pivot_roundtrip("PIVOT Cities ON Country || '_' || Name USING SUM(Population)");
58358    }
58359
58360    #[test]
58361    fn test_pivot_roundtrip_duckdb_multiple_group_by() {
58362        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) GROUP BY Country, Name");
58363    }
58364
58365    #[test]
58366    fn test_pivot_roundtrip_duckdb_first() {
58367        assert_pivot_roundtrip("PIVOT Cities ON Year USING FIRST(Population)");
58368    }
58369
58370    #[test]
58371    fn test_unpivot_roundtrip_duckdb_basic() {
58372        assert_pivot_roundtrip(
58373            "UNPIVOT monthly_sales ON jan, feb, mar, apr, may, jun INTO NAME month VALUE sales",
58374        );
58375    }
58376
58377    #[test]
58378    fn test_unpivot_roundtrip_duckdb_subquery() {
58379        assert_pivot_roundtrip("UNPIVOT (SELECT 1 AS col1, 2 AS col2) ON foo, bar");
58380    }
58381
58382    #[test]
58383    fn test_pivot_roundtrip_duckdb_cte() {
58384        assert_pivot_roundtrip("WITH pivot_alias AS (PIVOT Cities ON Year USING SUM(Population) GROUP BY Country) SELECT * FROM pivot_alias");
58385    }
58386
58387    #[test]
58388    fn test_pivot_roundtrip_duckdb_subquery() {
58389        assert_pivot_roundtrip("SELECT * FROM (PIVOT Cities ON Year USING SUM(Population) GROUP BY Country) AS pivot_alias");
58390    }
58391
58392    #[test]
58393    fn test_pivot_roundtrip_standard_from() {
58394        assert_pivot_roundtrip("SELECT * FROM cities PIVOT(SUM(population) FOR year IN (2000, 2010, 2020) GROUP BY country)");
58395    }
58396
58397    #[test]
58398    fn test_pivot_roundtrip_standard_bare_in() {
58399        assert_pivot_roundtrip("SELECT * FROM t PIVOT(SUM(y) FOR foo IN y_enum)");
58400    }
58401
58402    #[test]
58403    fn test_unpivot_roundtrip_bigquery() {
58404        assert_pivot_roundtrip_bq("SELECT * FROM q UNPIVOT(values FOR quarter IN (b, c))");
58405    }
58406
58407    #[test]
58408    fn test_pivot_roundtrip_bigquery_aliases() {
58409        assert_pivot_roundtrip_bq("SELECT cars, apples FROM some_table PIVOT(SUM(total_counts) FOR products IN ('general.cars' AS cars, 'food.apples' AS apples))");
58410    }
58411
58412    #[test]
58413    fn test_unpivot_roundtrip_bigquery_parens() {
58414        assert_pivot_roundtrip_bq(
58415            "SELECT * FROM (SELECT * FROM `t`) AS a UNPIVOT((c) FOR c_name IN (v1, v2))",
58416        );
58417    }
58418
58419    #[test]
58420    fn test_pivot_roundtrip_bigquery_multi_agg() {
58421        // Note: BigQuery fixture expects implicit aliases to become explicit AS
58422        let sql = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) AS d, COUNT(*) AS e FOR c IN ('x', 'y'))";
58423        assert_pivot_roundtrip_bq(sql);
58424    }
58425
58426    // Additional fixture tests for UNPIVOT with COLUMNS and grouped ON
58427    #[test]
58428    fn test_unpivot_roundtrip_duckdb_columns_exclude() {
58429        assert_pivot_roundtrip(
58430            "UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales",
58431        );
58432    }
58433
58434    #[test]
58435    fn test_unpivot_roundtrip_duckdb_grouped_columns() {
58436        assert_pivot_roundtrip("UNPIVOT monthly_sales ON (jan, feb, mar) AS q1, (apr, may, jun) AS q2 INTO NAME quarter VALUE month_1_sales, month_2_sales, month_3_sales");
58437    }
58438
58439    #[test]
58440    fn test_unpivot_roundtrip_duckdb_cte_columns() {
58441        assert_pivot_roundtrip("WITH unpivot_alias AS (UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales) SELECT * FROM unpivot_alias");
58442    }
58443
58444    #[test]
58445    fn test_unpivot_roundtrip_duckdb_subquery_columns() {
58446        assert_pivot_roundtrip("SELECT * FROM (UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales) AS unpivot_alias");
58447    }
58448
58449    #[test]
58450    fn test_pivot_roundtrip_duckdb_cte_with_columns() {
58451        assert_pivot_roundtrip("WITH cities(country, name, year, population) AS (SELECT 'NL', 'Amsterdam', 2000, 1005 UNION ALL SELECT 'US', 'Seattle', 2020, 738) PIVOT cities ON year USING SUM(population)");
58452    }
58453
58454    #[test]
58455    fn test_pivot_roundtrip_standard_first_with_alias() {
58456        // DuckDB fixture #73: comma before FOR is dropped in expected output
58457        let sql = "SELECT * FROM t PIVOT(FIRST(t) AS t, FOR quarter IN ('Q1', 'Q2'))";
58458        let expected = "SELECT * FROM t PIVOT(FIRST(t) AS t FOR quarter IN ('Q1', 'Q2'))";
58459        let parsed = crate::parse(sql, crate::DialectType::DuckDB);
58460        assert!(
58461            parsed.is_ok(),
58462            "Failed to parse: {} - {:?}",
58463            sql,
58464            parsed.err()
58465        );
58466        let stmts = parsed.unwrap();
58467        assert_eq!(stmts.len(), 1);
58468        let generated = crate::generate(&stmts[0], crate::DialectType::DuckDB);
58469        assert!(
58470            generated.is_ok(),
58471            "Failed to generate: {} - {:?}",
58472            sql,
58473            generated.err()
58474        );
58475        let result = generated.unwrap();
58476        assert_eq!(result.trim(), expected, "Round-trip mismatch");
58477    }
58478
58479    #[test]
58480    fn test_pivot_roundtrip_bigquery_implicit_alias() {
58481        // BigQuery fixture #134: implicit aliases become explicit AS
58482        let sql = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) d, COUNT(*) e FOR c IN ('x', 'y'))";
58483        let expected = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) AS d, COUNT(*) AS e FOR c IN ('x', 'y'))";
58484        let parsed = crate::parse(sql, crate::DialectType::BigQuery);
58485        assert!(
58486            parsed.is_ok(),
58487            "Failed to parse: {} - {:?}",
58488            sql,
58489            parsed.err()
58490        );
58491        let stmts = parsed.unwrap();
58492        assert_eq!(stmts.len(), 1);
58493        let generated = crate::generate(&stmts[0], crate::DialectType::BigQuery);
58494        assert!(
58495            generated.is_ok(),
58496            "Failed to generate: {} - {:?}",
58497            sql,
58498            generated.err()
58499        );
58500        let result = generated.unwrap();
58501        assert_eq!(result.trim(), expected, "Round-trip mismatch");
58502    }
58503
58504    #[test]
58505    fn test_duckdb_struct_enum_union_row_types() {
58506        use crate::DialectType;
58507
58508        // Helper to test roundtrip with DuckDB dialect - runs in a thread with larger stack
58509        fn check(sql: &str, expected: Option<&str>) {
58510            let sql = sql.to_string();
58511            let expected = expected.map(|s| s.to_string());
58512            let result = std::thread::Builder::new()
58513                .stack_size(16 * 1024 * 1024) // 16MB stack
58514                .spawn(move || {
58515                    let expected_out = expected.as_deref().unwrap_or(&sql);
58516                    let parsed = crate::parse(&sql, DialectType::DuckDB);
58517                    assert!(
58518                        parsed.is_ok(),
58519                        "Failed to parse: {} - {:?}",
58520                        sql,
58521                        parsed.err()
58522                    );
58523                    let stmts = parsed.unwrap();
58524                    assert!(!stmts.is_empty(), "No statements parsed: {}", sql);
58525                    let generated = crate::generate(&stmts[0], DialectType::DuckDB);
58526                    assert!(
58527                        generated.is_ok(),
58528                        "Failed to generate: {} - {:?}",
58529                        sql,
58530                        generated.err()
58531                    );
58532                    let result = generated.unwrap();
58533                    assert_eq!(result.trim(), expected_out, "Mismatch for: {}", sql);
58534                })
58535                .expect("Failed to spawn test thread")
58536                .join();
58537            assert!(result.is_ok(), "Test thread panicked");
58538        }
58539
58540        // UNION type
58541        check("CREATE TABLE tbl1 (u UNION(num INT, str TEXT))", None);
58542        // ENUM type
58543        check(
58544            "CREATE TABLE color (name ENUM('RED', 'GREEN', 'BLUE'))",
58545            None,
58546        );
58547        // ROW type -> STRUCT
58548        check(
58549            "SELECT CAST(ROW(1, 2) AS ROW(a INTEGER, b INTEGER))",
58550            Some("SELECT CAST(ROW(1, 2) AS STRUCT(a INT, b INT))"),
58551        );
58552        // STRUCT with parens
58553        check("CAST(x AS STRUCT(number BIGINT))", None);
58554        // STRUCT with quoted field names
58555        check(
58556            "CAST({'i': 1, 's': 'foo'} AS STRUCT(\"s\" TEXT, \"i\" INT))",
58557            None,
58558        );
58559        // Nested STRUCT
58560        check(
58561            "CAST(ROW(1, ROW(1)) AS STRUCT(number BIGINT, row STRUCT(number BIGINT)))",
58562            None,
58563        );
58564        // STRUCT with array suffix - test just the type parsing part
58565        // Note: STRUCT_PACK -> struct literal transform is a separate feature
58566        check("CAST(x AS STRUCT(a BIGINT)[][])", None);
58567        check("CAST(x AS STRUCT(a BIGINT)[])", None);
58568        // Double-colon cast with STRUCT type
58569        check("CAST({'a': 'b'} AS STRUCT(a TEXT))", None);
58570    }
58571
58572    // Helper for roundtrip identity tests
58573    fn roundtrip(sql: &str) -> String {
58574        let ast =
58575            Parser::parse_sql(sql).unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
58576        crate::generator::Generator::sql(&ast[0])
58577            .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e))
58578    }
58579
58580    fn assert_roundtrip(sql: &str) {
58581        let result = roundtrip(sql);
58582        assert_eq!(result, sql, "\n  Input:    {}\n  Output:   {}", sql, result);
58583    }
58584
58585    fn assert_roundtrip_expected(sql: &str, expected: &str) {
58586        let result = roundtrip(sql);
58587        assert_eq!(
58588            result, expected,
58589            "\n  Input:    {}\n  Expected: {}\n  Output:   {}",
58590            sql, expected, result
58591        );
58592    }
58593
58594    #[test]
58595    fn test_xmlelement_basic() {
58596        assert_roundtrip("SELECT XMLELEMENT(NAME foo)");
58597    }
58598
58599    #[test]
58600    fn test_xmlelement_with_xmlattributes() {
58601        assert_roundtrip("SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('xyz' AS bar))");
58602    }
58603
58604    #[test]
58605    fn test_xmlelement_with_multiple_attrs() {
58606        assert_roundtrip("SELECT XMLELEMENT(NAME test, XMLATTRIBUTES(a, b)) FROM test");
58607    }
58608
58609    #[test]
58610    fn test_xmlelement_with_content() {
58611        assert_roundtrip(
58612            "SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES(CURRENT_DATE AS bar), 'cont', 'ent')",
58613        );
58614    }
58615
58616    #[test]
58617    fn test_xmlelement_nested() {
58618        assert_roundtrip("SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('xyz' AS bar), XMLELEMENT(NAME abc), XMLCOMMENT('test'), XMLELEMENT(NAME xyz))");
58619    }
58620
58621    #[test]
58622    fn test_on_conflict_do_update() {
58623        assert_roundtrip("INSERT INTO newtable AS t(a, b, c) VALUES (1, 2, 3) ON CONFLICT(c) DO UPDATE SET a = t.a + 1 WHERE t.a < 1");
58624    }
58625
58626    #[test]
58627    fn test_on_conflict_do_nothing() {
58628        // ON CONFLICT(id) is the canonical form (no space before paren)
58629        assert_roundtrip_expected(
58630            "INSERT INTO test (id, name) VALUES (1, 'test') ON CONFLICT (id) DO NOTHING",
58631            "INSERT INTO test (id, name) VALUES (1, 'test') ON CONFLICT(id) DO NOTHING",
58632        );
58633    }
58634
58635    #[test]
58636    fn test_truncate_restart_identity() {
58637        assert_roundtrip("TRUNCATE TABLE t1 RESTART IDENTITY");
58638    }
58639
58640    #[test]
58641    fn test_truncate_restart_identity_restrict() {
58642        assert_roundtrip("TRUNCATE TABLE t1 RESTART IDENTITY RESTRICT");
58643    }
58644
58645    #[test]
58646    fn test_insert_by_name() {
58647        assert_roundtrip("INSERT INTO x BY NAME SELECT 1 AS y");
58648    }
58649
58650    #[test]
58651    fn test_insert_default_values_returning() {
58652        assert_roundtrip("INSERT INTO t DEFAULT VALUES RETURNING (c1)");
58653    }
58654
58655    #[test]
58656    fn test_union_all_by_name() {
58657        assert_roundtrip("SELECT 1 AS x UNION ALL BY NAME SELECT 2 AS x");
58658    }
58659
58660    #[test]
58661    fn test_minus_as_except() {
58662        // MINUS is Oracle/Redshift syntax for EXCEPT
58663        assert_roundtrip_expected(
58664            "SELECT foo, bar FROM table_1 MINUS SELECT foo, bar FROM table_2",
58665            "SELECT foo, bar FROM table_1 EXCEPT SELECT foo, bar FROM table_2",
58666        );
58667    }
58668
58669    #[test]
58670    fn test_filter_without_where() {
58671        assert_roundtrip_expected(
58672            "SELECT SUM(x) FILTER (x = 1)",
58673            "SELECT SUM(x) FILTER(WHERE x = 1)",
58674        );
58675    }
58676
58677    #[test]
58678    fn test_comment_on_materialized_view() {
58679        assert_roundtrip("COMMENT ON MATERIALIZED VIEW my_view IS 'this'");
58680    }
58681
58682    #[test]
58683    fn test_create_index_concurrently() {
58684        assert_roundtrip("CREATE INDEX CONCURRENTLY idx ON t(c)");
58685    }
58686
58687    #[test]
58688    fn test_create_index_if_not_exists() {
58689        assert_roundtrip("CREATE INDEX IF NOT EXISTS idx ON t(c)");
58690    }
58691
58692    #[test]
58693    fn test_alter_table_partition_hive() {
58694        // Hive: ALTER TABLE x PARTITION(y=z) ADD COLUMN a VARCHAR(10)
58695        assert_roundtrip("ALTER TABLE x PARTITION(y = z) ADD COLUMN a VARCHAR(10)");
58696    }
58697
58698    #[test]
58699    fn test_alter_table_change_column_hive() {
58700        // Hive/MySQL: CHANGE COLUMN old_name new_name data_type
58701        assert_roundtrip("ALTER TABLE x CHANGE COLUMN a a VARCHAR(10)");
58702    }
58703
58704    #[test]
58705    fn test_alter_table_add_columns_hive() {
58706        // Hive/Spark: ADD COLUMNS (col1 TYPE, col2 TYPE)
58707        assert_roundtrip("ALTER TABLE X ADD COLUMNS (y INT, z STRING)");
58708    }
58709
58710    #[test]
58711    fn test_alter_table_add_columns_cascade_hive() {
58712        // Hive/Spark: ADD COLUMNS (col1 TYPE, col2 TYPE) CASCADE
58713        assert_roundtrip("ALTER TABLE X ADD COLUMNS (y INT, z STRING) CASCADE");
58714    }
58715
58716    #[test]
58717    fn test_group_by_with_cube() {
58718        // Hive/MySQL: GROUP BY ... WITH CUBE
58719        let sql = "SELECT key, value FROM T1 GROUP BY key, value WITH CUBE";
58720        let result = Parser::parse_sql(sql).unwrap();
58721        let select = result[0].as_select().unwrap();
58722
58723        if let Some(group_by) = &select.group_by {
58724            // Debug: print the expressions
58725            eprintln!("GROUP BY expressions: {:?}", group_by.expressions);
58726
58727            // Check if there's a Cube expression with empty expressions
58728            let has_cube = group_by.expressions.iter().any(|e| {
58729                if let Expression::Cube(c) = e {
58730                    c.expressions.is_empty()
58731                } else {
58732                    false
58733                }
58734            });
58735            assert!(
58736                has_cube,
58737                "Should have a Cube expression with empty expressions in GROUP BY"
58738            );
58739        } else {
58740            panic!("Should have GROUP BY clause");
58741        }
58742    }
58743
58744    #[test]
58745    fn test_group_by_with_rollup() {
58746        // Hive/MySQL: GROUP BY ... WITH ROLLUP
58747        let sql = "SELECT key, value FROM T1 GROUP BY key, value WITH ROLLUP";
58748        let result = Parser::parse_sql(sql).unwrap();
58749        let select = result[0].as_select().unwrap();
58750
58751        if let Some(group_by) = &select.group_by {
58752            // Check if there's a Rollup expression with empty expressions
58753            let has_rollup = group_by.expressions.iter().any(|e| {
58754                if let Expression::Rollup(r) = e {
58755                    r.expressions.is_empty()
58756                } else {
58757                    false
58758                }
58759            });
58760            assert!(
58761                has_rollup,
58762                "Should have a Rollup expression with empty expressions in GROUP BY"
58763            );
58764        } else {
58765            panic!("Should have GROUP BY clause");
58766        }
58767    }
58768
58769    #[test]
58770    fn test_opendatasource_dot_access() {
58771        use crate::dialects::DialectType;
58772        use crate::transpile;
58773
58774        // OPENDATASOURCE(...).Catalog.dbo.Products — 3-part dot access
58775        let sql =
58776            "SELECT * FROM OPENDATASOURCE('SQLNCLI', 'Data Source=remote;').Catalog.dbo.Products";
58777        let result = transpile(sql, DialectType::TSQL, DialectType::TSQL).unwrap();
58778        assert_eq!(result[0], sql);
58779
58780        // 2-part dot access
58781        let sql2 = "SELECT * FROM OPENDATASOURCE('SQLNCLI', 'x').schema1.table1";
58782        let result2 = transpile(sql2, DialectType::TSQL, DialectType::TSQL).unwrap();
58783        assert_eq!(result2[0], sql2);
58784
58785        // 1-part dot access
58786        let sql3 = "SELECT * FROM OPENDATASOURCE('SQLNCLI', 'x').table1";
58787        let result3 = transpile(sql3, DialectType::TSQL, DialectType::TSQL).unwrap();
58788        assert_eq!(result3[0], sql3);
58789
58790        // No dot access (should still work as plain function)
58791        let sql4 = "SELECT * FROM OPENDATASOURCE('SQLNCLI', 'x')";
58792        let result4 = transpile(sql4, DialectType::TSQL, DialectType::TSQL).unwrap();
58793        assert_eq!(result4[0], sql4);
58794    }
58795
58796    #[test]
58797    fn test_exec_output_param() {
58798        use crate::dialects::DialectType;
58799        use crate::transpile;
58800
58801        // OUTPUT parameter
58802        let sql = "EXECUTE sp_CountOrders @region = 'US', @total = @count OUTPUT";
58803        let result = transpile(sql, DialectType::TSQL, DialectType::TSQL);
58804        assert!(
58805            result.is_ok(),
58806            "OUTPUT param should parse: {:?}",
58807            result.err()
58808        );
58809        assert_eq!(result.unwrap()[0], sql);
58810
58811        // WITH RESULT SETS (opaque — stored as Command)
58812        let sql2 = "EXEC sp_GetReport WITH RESULT SETS ((id INT, name NVARCHAR(100)))";
58813        let result2 = Parser::parse_sql(sql2);
58814        assert!(
58815            result2.is_ok(),
58816            "RESULT SETS should parse: {:?}",
58817            result2.err()
58818        );
58819
58820        // Dynamic SQL: EXECUTE (@sql)
58821        let sql3 = "EXECUTE (@sql)";
58822        let result3 = transpile(sql3, DialectType::TSQL, DialectType::TSQL);
58823        assert!(
58824            result3.is_ok(),
58825            "Dynamic SQL should parse: {:?}",
58826            result3.err()
58827        );
58828    }
58829}
58830
58831#[cfg(test)]
58832mod join_marker_tests {
58833    use super::*;
58834    use crate::dialects::DialectType;
58835
58836    #[test]
58837    fn test_oracle_join_marker_simple() {
58838        let sql = "select a.baz from a where a.baz = b.baz (+)";
58839        let result = Parser::parse_sql(sql);
58840        println!("Result: {:?}", result);
58841        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58842    }
58843
58844    #[test]
58845    fn test_oracle_join_marker_with_comma_join_and_aliases() {
58846        let sql = "SELECT e1.x, e2.x FROM e e1, e e2 WHERE e1.y = e2.y (+)";
58847        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58848        println!("Result: {:?}", result);
58849        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58850    }
58851
58852    #[test]
58853    fn test_oracle_xmltable_with_quoted_dot_columns() {
58854        let sql = "SELECT warehouse_name warehouse,\n   warehouse2.\"Water\", warehouse2.\"Rail\"\n   FROM warehouses,\n   XMLTABLE('/Warehouse'\n      PASSING warehouses.warehouse_spec\n      COLUMNS\n         \"Water\" varchar2(6) PATH 'WaterAccess',\n         \"Rail\" varchar2(6) PATH 'RailAccess')\n      warehouse2";
58855        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58856        println!("Result: {:?}", result);
58857        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58858    }
58859
58860    #[test]
58861    fn test_optimize_table_mysql() {
58862        use crate::dialects::DialectType;
58863        use crate::transpile;
58864
58865        // Multi-statement: TRUNCATE + OPTIMIZE
58866        let sql1 = "TRUNCATE TABLE session_logs";
58867        let r1 = transpile(sql1, DialectType::MySQL, DialectType::MySQL);
58868        assert!(r1.is_ok(), "TRUNCATE should parse: {:?}", r1.err());
58869
58870        let sql2 = "OPTIMIZE TABLE temp_exports";
58871        let r2 = transpile(sql2, DialectType::MySQL, DialectType::MySQL);
58872        assert!(r2.is_ok(), "OPTIMIZE should parse: {:?}", r2.err());
58873        assert_eq!(r2.unwrap()[0], sql2);
58874    }
58875
58876    #[test]
58877    fn test_mysql_index_hints() {
58878        use crate::dialects::DialectType;
58879        use crate::transpile;
58880
58881        // USE INDEX with alias
58882        let sql1 = "SELECT * FROM t e USE INDEX (idx1) WHERE a = 1";
58883        let r1 = transpile(sql1, DialectType::MySQL, DialectType::MySQL);
58884        assert!(r1.is_ok(), "USE INDEX with alias: {:?}", r1.err());
58885
58886        // IGNORE INDEX in JOIN with PRIMARY keyword
58887        let sql2 = "SELECT * FROM t1 JOIN t2 IGNORE INDEX (PRIMARY) ON t1.id = t2.id";
58888        let r2 = transpile(sql2, DialectType::MySQL, DialectType::MySQL);
58889        assert!(r2.is_ok(), "IGNORE INDEX PRIMARY: {:?}", r2.err());
58890
58891        // Full example from issue
58892        let sql3 = "SELECT e.name, d.department_name FROM employees e USE INDEX (idx_dept, idx_salary) JOIN departments d IGNORE INDEX (PRIMARY) ON e.department_id = d.department_id WHERE e.salary > 60000";
58893        let r3 = transpile(sql3, DialectType::MySQL, DialectType::MySQL);
58894        assert!(r3.is_ok(), "Full example: {:?}", r3.err());
58895    }
58896
58897    #[test]
58898    fn test_oracle_quoted_dot_projection() {
58899        let sql = "SELECT warehouse2.\"Water\", warehouse2.\"Rail\" FROM warehouses warehouse2";
58900        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58901        println!("Result: {:?}", result);
58902        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58903    }
58904
58905    #[test]
58906    fn test_oracle_xmltable_columns_only() {
58907        let sql = "SELECT * FROM XMLTABLE('/Warehouse' PASSING warehouses.warehouse_spec COLUMNS \"Water\" varchar2(6) PATH 'WaterAccess', \"Rail\" varchar2(6) PATH 'RailAccess') warehouse2";
58908        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58909        println!("Result: {:?}", result);
58910        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58911    }
58912
58913    #[test]
58914    fn test_spark_limit() {
58915        use crate::dialects::DialectType;
58916        use crate::transpile;
58917
58918        // Spark LIMIT should work
58919        let sql = "SELECT * FROM something LIMIT 100";
58920        let r = transpile(sql, DialectType::Spark, DialectType::Spark);
58921        assert!(r.is_ok(), "Spark LIMIT: {:?}", r.err());
58922        assert_eq!(r.unwrap()[0], sql);
58923
58924        // Hive LIMIT should work
58925        let r2 = transpile(sql, DialectType::Hive, DialectType::Hive);
58926        assert!(r2.is_ok(), "Hive LIMIT: {:?}", r2.err());
58927    }
58928
58929    #[test]
58930    fn test_oracle_projection_alias_then_quoted_dot() {
58931        let sql =
58932            "SELECT warehouse_name warehouse, warehouse2.\"Water\" FROM warehouses warehouse2";
58933        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58934        println!("Result: {:?}", result);
58935        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58936    }
58937}
58938
58939#[cfg(test)]
58940mod clickhouse_parser_regression_tests {
58941    use crate::dialects::DialectType;
58942
58943    #[test]
58944    fn test_clickhouse_select_format_clause_not_alias() {
58945        let sql = "SELECT 1 FORMAT TabSeparated";
58946        let result = crate::dialects::Dialect::get(DialectType::ClickHouse).parse(sql);
58947        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58948    }
58949
58950    #[test]
58951    fn test_clickhouse_projection_select_group_by_parses() {
58952        let sql = "CREATE TABLE t (a String, b String, c UInt64, PROJECTION p1 (SELECT a, sum(c) GROUP BY a, b), PROJECTION p2 (SELECT b, sum(c) GROUP BY b)) ENGINE=MergeTree()";
58953        let result = crate::dialects::Dialect::get(DialectType::ClickHouse).parse(sql);
58954        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58955    }
58956
58957    /// ClickHouse ternary operator AST structure tests.
58958    /// Ported from Python sqlglot: tests/dialects/test_clickhouse.py::test_ternary (lines 765-778).
58959    /// Verifies that `x ? (y ? 1 : 2) : 3` parses into nested IfFunc nodes
58960    /// with the correct AST shape.
58961    #[test]
58962    fn test_clickhouse_ternary_ast_structure() {
58963        use crate::expressions::Expression;
58964
58965        let result = crate::parse_one("x ? (y ? 1 : 2) : 3", DialectType::ClickHouse);
58966        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58967        let ternary = result.unwrap();
58968
58969        // Root should be IfFunc
58970        let if_func = match &ternary {
58971            Expression::IfFunc(f) => f,
58972            other => panic!("Expected IfFunc, got {:?}", std::mem::discriminant(other)),
58973        };
58974
58975        // this (condition) should be Column "x"
58976        assert!(
58977            matches!(&if_func.condition, Expression::Column(_)),
58978            "Expected condition to be Column, got {:?}",
58979            std::mem::discriminant(&if_func.condition)
58980        );
58981
58982        // true branch should be Paren
58983        assert!(
58984            matches!(&if_func.true_value, Expression::Paren(_)),
58985            "Expected true_value to be Paren, got {:?}",
58986            std::mem::discriminant(&if_func.true_value)
58987        );
58988
58989        // false branch should be Literal
58990        let false_value = if_func.false_value.as_ref().expect("Expected false_value");
58991        assert!(
58992            matches!(false_value, Expression::Literal(_)),
58993            "Expected false_value to be Literal, got {:?}",
58994            std::mem::discriminant(false_value)
58995        );
58996
58997        // Inside the Paren, the nested ternary should also be IfFunc
58998        let inner_paren = match &if_func.true_value {
58999            Expression::Paren(p) => p,
59000            _ => unreachable!(),
59001        };
59002        let nested_if = match &inner_paren.this {
59003            Expression::IfFunc(f) => f,
59004            other => panic!(
59005                "Expected nested IfFunc, got {:?}",
59006                std::mem::discriminant(other)
59007            ),
59008        };
59009
59010        // Nested condition should be Column "y"
59011        assert!(
59012            matches!(&nested_if.condition, Expression::Column(_)),
59013            "Expected nested condition to be Column, got {:?}",
59014            std::mem::discriminant(&nested_if.condition)
59015        );
59016
59017        // Nested true should be Literal 1
59018        assert!(
59019            matches!(&nested_if.true_value, Expression::Literal(_)),
59020            "Expected nested true_value to be Literal, got {:?}",
59021            std::mem::discriminant(&nested_if.true_value)
59022        );
59023
59024        // Nested false should be Literal 2
59025        let nested_false = nested_if
59026            .false_value
59027            .as_ref()
59028            .expect("Expected nested false_value");
59029        assert!(
59030            matches!(nested_false, Expression::Literal(_)),
59031            "Expected nested false_value to be Literal, got {:?}",
59032            std::mem::discriminant(nested_false)
59033        );
59034    }
59035
59036    /// Verify that `a AND b ? 1 : 2` has And as the ternary condition
59037    /// (AND binds tighter than ?).
59038    /// Ported from Python sqlglot: test_clickhouse.py line 778.
59039    #[test]
59040    fn test_clickhouse_ternary_and_precedence() {
59041        use crate::expressions::Expression;
59042
59043        let result = crate::parse_one("a and b ? 1 : 2", DialectType::ClickHouse);
59044        assert!(result.is_ok(), "Parse error: {:?}", result.err());
59045        let ternary = result.unwrap();
59046
59047        let if_func = match &ternary {
59048            Expression::IfFunc(f) => f,
59049            other => panic!("Expected IfFunc, got {:?}", std::mem::discriminant(other)),
59050        };
59051
59052        // The condition should be And (not just Column "b")
59053        assert!(
59054            matches!(&if_func.condition, Expression::And(_)),
59055            "Expected condition to be And, got {:?}",
59056            std::mem::discriminant(&if_func.condition)
59057        );
59058    }
59059
59060    #[test]
59061    fn test_parse_interval_bare_number_duckdb() {
59062        use crate::dialects::{Dialect, DialectType};
59063        let sql = "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL 3 DAY";
59064        let d = Dialect::get(DialectType::DuckDB);
59065        match d.parse(sql) {
59066            Ok(result) => {
59067                assert!(!result.is_empty(), "Should parse to at least one statement");
59068                // Test transpilation to DuckDB target - should normalize number to quoted string
59069                let output_duckdb = d.transpile_to(sql, DialectType::DuckDB).unwrap();
59070                assert_eq!(
59071                    output_duckdb[0],
59072                    "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL '3' DAY",
59073                    "DuckDB output should have quoted interval value"
59074                );
59075                // Test transpilation to Hive target
59076                let output_hive = d.transpile_to(sql, DialectType::Hive).unwrap();
59077                assert_eq!(
59078                    output_hive[0], "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL '3' DAY",
59079                    "Hive output should have quoted interval value"
59080                );
59081            }
59082            Err(e) => panic!("Failed to parse DuckDB INTERVAL 3 DAY: {}", e),
59083        }
59084    }
59085}