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        let result = self.parse_select_body()?;
1368        // Check for set operations (UNION, INTERSECT, EXCEPT)
1369        self.parse_set_operation(result)
1370    }
1371
1372    /// Parse a SELECT statement body without consuming trailing set operations.
1373    /// Used by `parse_select_or_paren_select` to avoid mutual recursion with
1374    /// `parse_set_operation`, which handles set-op chaining iteratively.
1375    fn parse_select_body(&mut self) -> Result<Expression> {
1376        // Capture the SELECT token to get its comments
1377        let select_token = self.expect(TokenType::Select)?;
1378        let leading_comments = select_token.comments;
1379        let post_select_comments = select_token.trailing_comments;
1380
1381        // Parse query hint /*+ ... */ if present (comes immediately after SELECT)
1382        let hint = if self.check(TokenType::Hint) {
1383            Some(self.parse_hint()?)
1384        } else {
1385            None
1386        };
1387
1388        // Parse TOP clause (SQL Server style - comes before DISTINCT)
1389        // But not if TOP is followed by DOT (e.g., SELECT top.x - top is a table alias)
1390        let top = if self.check(TokenType::Top)
1391            && !self.check_next(TokenType::Dot)
1392            && self.match_token(TokenType::Top)
1393        {
1394            // TOP can have parentheses: TOP (10) or without: TOP 10
1395            let (amount, parenthesized) = if self.match_token(TokenType::LParen) {
1396                let expr = self.parse_expression()?;
1397                self.expect(TokenType::RParen)?;
1398                (expr, true)
1399            } else {
1400                (self.parse_primary()?, false)
1401            };
1402            let percent = self.match_token(TokenType::Percent);
1403            let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1404            Some(Top {
1405                this: amount,
1406                percent,
1407                with_ties,
1408                parenthesized,
1409            })
1410        } else {
1411            None
1412        };
1413
1414        // Parse DISTINCT / DISTINCT ON / DISTINCTROW / ALL
1415        // Oracle: UNIQUE is equivalent to DISTINCT (SELECT UNIQUE ... is old-style Oracle syntax)
1416        let is_distinct_token = self.match_token(TokenType::Distinct)
1417            || (matches!(
1418                self.config.dialect,
1419                Some(crate::dialects::DialectType::Oracle)
1420            ) && self.match_token(TokenType::Unique));
1421        let (distinct, distinct_on) = if is_distinct_token {
1422            if self.match_token(TokenType::On) {
1423                // DISTINCT ON (expr, ...)
1424                self.expect(TokenType::LParen)?;
1425                let exprs = self.parse_expression_list()?;
1426                self.expect(TokenType::RParen)?;
1427                (true, Some(exprs))
1428            } else {
1429                (true, None)
1430            }
1431        } else if self.check_identifier("DISTINCTROW") {
1432            // MySQL DISTINCTROW - equivalent to DISTINCT
1433            self.skip();
1434            (true, None)
1435        } else {
1436            // Only consume ALL if it's the SELECT ALL modifier, not if it's a column reference like "all.count"
1437            if self.check(TokenType::All) && !self.check_next(TokenType::Dot) {
1438                self.skip();
1439            }
1440            (false, None)
1441        };
1442
1443        // TSQL: SELECT DISTINCT TOP n - TOP can come after DISTINCT
1444        // If no TOP was parsed before DISTINCT, check for TOP after DISTINCT
1445        let top = if top.is_none()
1446            && self.check(TokenType::Top)
1447            && !self.check_next(TokenType::Dot)
1448            && self.match_token(TokenType::Top)
1449        {
1450            let (amount, parenthesized) = if self.match_token(TokenType::LParen) {
1451                let expr = self.parse_expression()?;
1452                self.expect(TokenType::RParen)?;
1453                (expr, true)
1454            } else {
1455                (self.parse_primary()?, false)
1456            };
1457            let percent = self.match_token(TokenType::Percent);
1458            let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1459            Some(Top {
1460                this: amount,
1461                percent,
1462                with_ties,
1463                parenthesized,
1464            })
1465        } else {
1466            top
1467        };
1468
1469        // Parse MySQL operation modifiers (HIGH_PRIORITY, STRAIGHT_JOIN, SQL_CALC_FOUND_ROWS, etc.)
1470        // These appear after DISTINCT/ALL and before the projections
1471        // Only apply for MySQL-family dialects - other dialects treat these as identifiers
1472        let mut operation_modifiers = Vec::new();
1473        let is_mysql_dialect = matches!(
1474            self.config.dialect,
1475            Some(crate::dialects::DialectType::MySQL)
1476                | Some(crate::dialects::DialectType::SingleStore)
1477                | Some(crate::dialects::DialectType::StarRocks)
1478                | Some(crate::dialects::DialectType::TiDB)
1479                | Some(crate::dialects::DialectType::Doris)
1480        );
1481        if is_mysql_dialect {
1482            const MYSQL_MODIFIERS: &[&str] = &[
1483                "HIGH_PRIORITY",
1484                "STRAIGHT_JOIN",
1485                "SQL_SMALL_RESULT",
1486                "SQL_BIG_RESULT",
1487                "SQL_BUFFER_RESULT",
1488                "SQL_NO_CACHE",
1489                "SQL_CALC_FOUND_ROWS",
1490            ];
1491            loop {
1492                if self.check(TokenType::StraightJoin) {
1493                    self.skip();
1494                    operation_modifiers.push("STRAIGHT_JOIN".to_string());
1495                } else if self.check(TokenType::Var) {
1496                    let upper = self.peek().text.to_ascii_uppercase();
1497                    if MYSQL_MODIFIERS.contains(&upper.as_str()) {
1498                        self.skip();
1499                        operation_modifiers.push(upper);
1500                    } else {
1501                        break;
1502                    }
1503                } else {
1504                    break;
1505                }
1506            }
1507        }
1508
1509        // Parse BigQuery SELECT AS STRUCT / SELECT AS VALUE
1510        let kind = if self.match_token(TokenType::As) {
1511            if self.match_identifier("STRUCT") {
1512                Some("STRUCT".to_string())
1513            } else if self.match_identifier("VALUE") {
1514                Some("VALUE".to_string())
1515            } else {
1516                // Not AS STRUCT/VALUE, backtrack the AS token
1517                self.current -= 1;
1518                None
1519            }
1520        } else {
1521            None
1522        };
1523
1524        // Parse select expressions
1525        let mut expressions = self.parse_select_expressions()?;
1526
1527        // Redshift: EXCLUDE clause at the end of the projection list
1528        // e.g., SELECT *, 4 AS col4 EXCLUDE (col2, col3) FROM ...
1529        // e.g., SELECT col1, *, col2 EXCLUDE(col3) FROM ...
1530        // e.g., SELECT *, 4 AS col4 EXCLUDE col2, col3 FROM ...
1531        // In Python sqlglot, this is handled by overriding _parse_projections in the Redshift parser.
1532        // The EXCLUDE clause is separate from * EXCLUDE — it applies to the entire projection list.
1533        let exclude = if matches!(
1534            self.config.dialect,
1535            Some(crate::dialects::DialectType::Redshift)
1536        ) {
1537            // Check if previous token was EXCLUDE (parsed as implicit alias).
1538            // e.g., SELECT *, 4 AS col4 EXCLUDE col2, col3 FROM ...
1539            //   → "col4 EXCLUDE" was parsed as (col4 aliased-as EXCLUDE), then "col2" as next projection
1540            //   → We need to strip the EXCLUDE alias from the last projection and retreat
1541            // Also handle: EXCLUDE was consumed as a bare column name if no AS was present
1542            let mut retreat_for_exclude = false;
1543            if let Some(last_expr) = expressions.last() {
1544                // Case: "4 AS col4 EXCLUDE" without parens — parsed as separate column "EXCLUDE"
1545                // Actually with the comma break, this won't happen. But "col2 EXCLUDE(col3)" might.
1546                match last_expr {
1547                    Expression::Alias(alias)
1548                        if alias.alias.name.eq_ignore_ascii_case("EXCLUDE") =>
1549                    {
1550                        // The last expression is "something AS EXCLUDE" or implicit alias EXCLUDE
1551                        // Strip the alias and check if EXCLUDE is followed by paren or identifier
1552                        if self.check(TokenType::LParen)
1553                            || self.is_identifier_token()
1554                            || self.is_safe_keyword_as_identifier()
1555                        {
1556                            // Strip the EXCLUDE alias from the last expression
1557                            let stripped = alias.this.clone();
1558                            if let Some(last) = expressions.last_mut() {
1559                                *last = stripped;
1560                            }
1561                            retreat_for_exclude = true;
1562                        }
1563                    }
1564                    _ => {}
1565                }
1566            }
1567
1568            if retreat_for_exclude || self.check(TokenType::Exclude) {
1569                if !retreat_for_exclude {
1570                    self.skip(); // consume EXCLUDE
1571                }
1572                // Parse EXCLUDE columns - with or without parens
1573                let mut exclude_cols = Vec::new();
1574                if self.match_token(TokenType::LParen) {
1575                    // Parenthesized list: EXCLUDE (col1, col2, ...)
1576                    loop {
1577                        let col_expr = self.parse_expression()?;
1578                        exclude_cols.push(col_expr);
1579                        if !self.match_token(TokenType::Comma) {
1580                            break;
1581                        }
1582                    }
1583                    self.match_token(TokenType::RParen);
1584                } else {
1585                    // Non-parenthesized: EXCLUDE col1, col2, ...
1586                    // Parse comma-separated identifiers until FROM or other clause boundary
1587                    loop {
1588                        if self.is_at_end()
1589                            || self.check(TokenType::From)
1590                            || self.check(TokenType::Where)
1591                            || self.check(TokenType::Semicolon)
1592                            || self.check(TokenType::RParen)
1593                        {
1594                            break;
1595                        }
1596                        let col_expr = self.parse_expression()?;
1597                        exclude_cols.push(col_expr);
1598                        if !self.match_token(TokenType::Comma) {
1599                            break;
1600                        }
1601                    }
1602                }
1603                if exclude_cols.is_empty() {
1604                    None
1605                } else {
1606                    Some(exclude_cols)
1607                }
1608            } else {
1609                None
1610            }
1611        } else {
1612            None
1613        };
1614
1615        // Parse INTO clause (SELECT ... INTO [TEMPORARY|UNLOGGED] table_name)
1616        // Also handles Oracle PL/SQL: BULK COLLECT INTO v1, v2, ...
1617        let into = if self.match_text_seq(&["BULK", "COLLECT", "INTO"]) {
1618            // Oracle PL/SQL: BULK COLLECT INTO var1, var2, ...
1619            // Parse target variables as a comma-separated list
1620            let mut target_expressions = vec![self.parse_expression()?];
1621            while self.match_token(TokenType::Comma) {
1622                target_expressions.push(self.parse_expression()?);
1623            }
1624            if target_expressions.len() == 1 {
1625                Some(SelectInto {
1626                    this: target_expressions.remove(0),
1627                    temporary: false,
1628                    unlogged: false,
1629                    bulk_collect: true,
1630                    expressions: Vec::new(),
1631                })
1632            } else {
1633                // Multiple targets - use first as `this` and rest as `expressions`
1634                // Actually, to match Python sqlglot behavior, store all in expressions
1635                Some(SelectInto {
1636                    this: Expression::Null(Null),
1637                    temporary: false,
1638                    unlogged: false,
1639                    bulk_collect: true,
1640                    expressions: target_expressions,
1641                })
1642            }
1643        } else if self.match_token(TokenType::Into) {
1644            // Check for TEMPORARY/TEMP/UNLOGGED keyword (PostgreSQL)
1645            let temporary = self.match_token(TokenType::Temporary) || self.match_identifier("TEMP");
1646            let unlogged = !temporary && self.match_identifier("UNLOGGED");
1647            // Parse first target (table name or PL/SQL variable)
1648            let table_name = self.parse_table_ref()?;
1649            // Oracle PL/SQL: SELECT ... INTO var1, var2, ... FROM ...
1650            // If followed by comma, parse additional target variables
1651            if self.match_token(TokenType::Comma) {
1652                let mut target_expressions = vec![Expression::Table(Box::new(table_name))];
1653                target_expressions.push(self.parse_expression()?);
1654                while self.match_token(TokenType::Comma) {
1655                    target_expressions.push(self.parse_expression()?);
1656                }
1657                Some(SelectInto {
1658                    this: Expression::Null(Null),
1659                    temporary,
1660                    unlogged,
1661                    bulk_collect: false,
1662                    expressions: target_expressions,
1663                })
1664            } else {
1665                Some(SelectInto {
1666                    this: Expression::Table(Box::new(table_name)),
1667                    temporary,
1668                    unlogged,
1669                    bulk_collect: false,
1670                    expressions: Vec::new(),
1671                })
1672            }
1673        } else {
1674            None
1675        };
1676
1677        // Parse FROM clause
1678        let from = if self.match_token(TokenType::From) {
1679            Some(self.parse_from()?)
1680        } else {
1681            None
1682        };
1683
1684        // Parse JOINs
1685        let mut joins = self.parse_joins()?;
1686
1687        // Handle PIVOT/UNPIVOT that comes after JOINs (e.g., SELECT * FROM a JOIN b ON ... PIVOT(...))
1688        // Store PIVOT/UNPIVOT in the last join's pivots field (this matches SQLGlot's semantics)
1689        while self.check(TokenType::Pivot) || self.check(TokenType::Unpivot) {
1690            if !joins.is_empty() {
1691                let last_idx = joins.len() - 1;
1692                // Parse the pivot/unpivot and store in the join's pivots vector
1693                // We pass a Null expression as the `this` since the pivot applies to the entire join result
1694                if self.match_token(TokenType::Pivot) {
1695                    let pivot = self.parse_pivot(Expression::Null(crate::expressions::Null))?;
1696                    joins[last_idx].pivots.push(pivot);
1697                } else if self.match_token(TokenType::Unpivot) {
1698                    let unpivot = self.parse_unpivot(Expression::Null(crate::expressions::Null))?;
1699                    joins[last_idx].pivots.push(unpivot);
1700                }
1701            } else {
1702                // No joins - break to avoid infinite loop
1703                break;
1704            }
1705        }
1706
1707        // Parse LATERAL VIEW clauses (Hive/Spark)
1708        let lateral_views = self.parse_lateral_views()?;
1709
1710        // Parse PREWHERE clause (ClickHouse specific)
1711        let prewhere = if self.match_token(TokenType::Prewhere) {
1712            Some(self.parse_expression()?)
1713        } else {
1714            None
1715        };
1716
1717        // Parse WHERE clause
1718        let mut where_clause = if self.match_token(TokenType::Where) {
1719            Some(Where {
1720                this: self.parse_expression()?,
1721            })
1722        } else {
1723            None
1724        };
1725
1726        // Parse CONNECT BY clause (Oracle hierarchical queries)
1727        let connect = self.parse_connect()?;
1728
1729        // Parse GROUP BY
1730        let group_by = if self.check(TokenType::Group) {
1731            let group_comments = self.current_leading_comments().to_vec();
1732            if self.match_keywords(&[TokenType::Group, TokenType::By]) {
1733                let mut gb = self.parse_group_by()?;
1734                gb.comments = group_comments;
1735                Some(gb)
1736            } else {
1737                None
1738            }
1739        } else if matches!(
1740            self.config.dialect,
1741            Some(crate::dialects::DialectType::ClickHouse)
1742        ) && self.check(TokenType::With)
1743            && (self.check_next_identifier("TOTALS")
1744                || self.check_next(TokenType::Rollup)
1745                || self.check_next(TokenType::Cube))
1746        {
1747            // ClickHouse: WITH TOTALS/ROLLUP/CUBE without GROUP BY
1748            self.skip(); // consume WITH
1749            let totals = self.match_identifier("TOTALS");
1750            let mut expressions = Vec::new();
1751            if self.match_token(TokenType::Rollup) {
1752                expressions.push(Expression::Rollup(Box::new(Rollup {
1753                    expressions: Vec::new(),
1754                })));
1755            } else if self.match_token(TokenType::Cube) {
1756                expressions.push(Expression::Cube(Box::new(Cube {
1757                    expressions: Vec::new(),
1758                })));
1759            }
1760            // Check for chained WITH TOTALS after WITH ROLLUP/CUBE
1761            if !totals && self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
1762                self.skip();
1763                self.skip();
1764            }
1765            Some(GroupBy {
1766                expressions,
1767                all: None,
1768                totals,
1769                comments: Vec::new(),
1770            })
1771        } else {
1772            None
1773        };
1774
1775        // Parse HAVING
1776        let having = if self.check(TokenType::Having) {
1777            let having_comments = self.current_leading_comments().to_vec();
1778            self.skip(); // consume HAVING
1779            Some(Having {
1780                this: self.parse_expression()?,
1781                comments: having_comments,
1782            })
1783        } else {
1784            None
1785        };
1786
1787        // Parse QUALIFY clause (Snowflake, BigQuery, DuckDB)
1788        // QUALIFY can appear before or after WINDOW clause
1789        let mut qualify = if self.match_token(TokenType::Qualify) {
1790            Some(Qualify {
1791                this: self.parse_expression()?,
1792            })
1793        } else {
1794            None
1795        };
1796
1797        // Parse WINDOW clause (named windows)
1798        // Only match WINDOW if followed by identifier AS ( (a real window definition)
1799        // Otherwise "window" may be a table alias (e.g., SELECT * FROM foo window)
1800        let windows = if self.check(TokenType::Window) && {
1801            let next_pos = self.current + 1;
1802            next_pos < self.tokens.len()
1803                && (self.tokens[next_pos].token_type == TokenType::Var
1804                    || self.tokens[next_pos].token_type == TokenType::Identifier)
1805        } {
1806            self.skip(); // consume WINDOW
1807            Some(self.parse_named_windows()?)
1808        } else {
1809            None
1810        };
1811
1812        // QUALIFY can also appear after WINDOW clause (DuckDB)
1813        let qualify_after_window = if qualify.is_none() && self.match_token(TokenType::Qualify) {
1814            qualify = Some(Qualify {
1815                this: self.parse_expression()?,
1816            });
1817            true
1818        } else {
1819            false
1820        };
1821
1822        // Parse DISTRIBUTE BY (Hive/Spark) - comes before SORT BY
1823        let distribute_by = if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
1824            Some(self.parse_distribute_by()?)
1825        } else {
1826            None
1827        };
1828
1829        // Parse CLUSTER BY (Hive/Spark)
1830        let cluster_by = if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
1831            Some(self.parse_cluster_by()?)
1832        } else {
1833            None
1834        };
1835
1836        // Parse SORT BY (Hive/Spark) - can come before ORDER BY
1837        let sort_by = if self.match_keywords(&[TokenType::Sort, TokenType::By]) {
1838            Some(self.parse_sort_by()?)
1839        } else {
1840            None
1841        };
1842
1843        // Parse ORDER BY or ORDER SIBLINGS BY (Oracle) - comes after SORT BY
1844        let order_by = if self.check(TokenType::Order) {
1845            let order_comments = self.current_leading_comments().to_vec();
1846            if self.match_keywords(&[TokenType::Order, TokenType::Siblings, TokenType::By]) {
1847                // ORDER SIBLINGS BY (Oracle hierarchical queries)
1848                let mut ob = self.parse_order_by_with_siblings(true)?;
1849                ob.comments = order_comments;
1850                Some(ob)
1851            } else if self.match_keywords(&[TokenType::Order, TokenType::By]) {
1852                let mut ob = self.parse_order_by()?;
1853                ob.comments = order_comments;
1854                Some(ob)
1855            } else {
1856                None
1857            }
1858        } else {
1859            None
1860        };
1861
1862        // Parse LIMIT (supports MySQL syntax: LIMIT offset, count)
1863        // DuckDB supports: LIMIT 10 PERCENT or LIMIT 10%
1864        // Capture trailing comments from the token before LIMIT (e.g., WHERE condition's last token)
1865        // These comments should be emitted after the LIMIT value, not before LIMIT.
1866        let pre_limit_comments = if self.check(TokenType::Limit) {
1867            let mut comments = self.previous_trailing_comments().to_vec();
1868            // Also capture leading comments on the LIMIT token (comments on a separate line before LIMIT)
1869            comments.extend_from_slice(self.current_leading_comments());
1870            comments
1871        } else {
1872            Vec::new()
1873        };
1874        let (limit, offset) = if self.match_token(TokenType::Limit) {
1875            // Clear the pre-LIMIT comments from the WHERE condition expression to avoid duplication
1876            if !pre_limit_comments.is_empty() {
1877                if let Some(ref mut w) = where_clause {
1878                    Self::clear_rightmost_trailing_comments(&mut w.this);
1879                }
1880            }
1881            // First try parse_unary to check for PERCENT/% modifier.
1882            // This avoids parse_expression consuming % as the modulo operator.
1883            // Both "PERCENT" and "%" tokens have TokenType::Percent, but we need to
1884            // distinguish PERCENT-as-modifier from %-as-modulo. "%" is PERCENT when
1885            // followed by a clause boundary (OFFSET, end, semicolon, etc.).
1886            let saved_pos = self.current;
1887            let (first_expr, has_percent) = {
1888                let unary_result = self.parse_unary();
1889                match unary_result {
1890                    Ok(expr) => {
1891                        if self.check(TokenType::Percent) && self.is_percent_modifier() {
1892                            // Found PERCENT keyword or % symbol used as PERCENT modifier
1893                            self.skip();
1894                            (expr, true)
1895                        } else {
1896                            // No PERCENT - backtrack and use full parse_expression
1897                            self.current = saved_pos;
1898                            let full_expr = self.parse_expression()?;
1899                            // Check again for PERCENT keyword (e.g., after complex expression)
1900                            let has_pct =
1901                                if self.check(TokenType::Percent) && self.is_percent_modifier() {
1902                                    self.skip();
1903                                    true
1904                                } else {
1905                                    false
1906                                };
1907                            (full_expr, has_pct)
1908                        }
1909                    }
1910                    Err(_) => {
1911                        // Unary parsing failed - backtrack and use parse_expression
1912                        self.current = saved_pos;
1913                        let full_expr = self.parse_expression()?;
1914                        let has_pct =
1915                            if self.check(TokenType::Percent) && self.is_percent_modifier() {
1916                                self.skip();
1917                                true
1918                            } else {
1919                                false
1920                            };
1921                        (full_expr, has_pct)
1922                    }
1923                }
1924            };
1925            // MySQL syntax: LIMIT offset, count
1926            if self.match_token(TokenType::Comma) {
1927                let second_expr = self.parse_expression()?;
1928                // First expression is offset, second is count
1929                (
1930                    Some(Limit {
1931                        this: second_expr,
1932                        percent: false,
1933                        comments: pre_limit_comments.clone(),
1934                    }),
1935                    Some(Offset {
1936                        this: first_expr,
1937                        rows: None,
1938                    }),
1939                )
1940            } else {
1941                // Standard: LIMIT count [PERCENT]
1942                (
1943                    Some(Limit {
1944                        this: first_expr,
1945                        percent: has_percent,
1946                        comments: pre_limit_comments,
1947                    }),
1948                    None,
1949                )
1950            }
1951        } else {
1952            (None, None)
1953        };
1954
1955        // WITH TIES after LIMIT (ClickHouse, DuckDB)
1956        if limit.is_some() {
1957            let _ = self.match_keywords(&[TokenType::With, TokenType::Ties]);
1958        }
1959
1960        // Parse OFFSET (if not already parsed from MySQL LIMIT syntax)
1961        // Standard SQL syntax: OFFSET n [ROW|ROWS]
1962        // Some dialects (Presto/Trino) support: OFFSET n LIMIT m
1963        let (limit, offset) = if offset.is_none() && self.match_token(TokenType::Offset) {
1964            let expr = self.parse_expression()?;
1965            // Consume optional ROW or ROWS keyword and track it
1966            let rows = if self.match_token(TokenType::Row) || self.match_token(TokenType::Rows) {
1967                Some(true)
1968            } else {
1969                None
1970            };
1971            let offset = Some(Offset { this: expr, rows });
1972
1973            // Check for LIMIT after OFFSET (Presto/Trino syntax: OFFSET n LIMIT m)
1974            let limit = if limit.is_none() && self.match_token(TokenType::Limit) {
1975                let limit_expr = self.parse_expression()?;
1976                Some(Limit {
1977                    this: limit_expr,
1978                    percent: false,
1979                    comments: Vec::new(),
1980                })
1981            } else {
1982                limit
1983            };
1984
1985            (limit, offset)
1986        } else {
1987            (limit, offset)
1988        };
1989
1990        // ClickHouse: LIMIT ... BY expressions
1991        let limit_by = if matches!(
1992            self.config.dialect,
1993            Some(crate::dialects::DialectType::ClickHouse)
1994        ) && limit.is_some()
1995            && self.match_token(TokenType::By)
1996        {
1997            let expressions = self.parse_expression_list()?;
1998            if expressions.is_empty() {
1999                return Err(self.parse_error("Expected expression after LIMIT BY"));
2000            }
2001            Some(expressions)
2002        } else {
2003            None
2004        };
2005
2006        // ClickHouse: second LIMIT after LIMIT BY (LIMIT n BY expr LIMIT m)
2007        // Also supports LIMIT offset, count syntax
2008        let (limit, offset) = if limit_by.is_some() && self.match_token(TokenType::Limit) {
2009            let first_expr = self.parse_expression()?;
2010            if self.match_token(TokenType::Comma) {
2011                // LIMIT offset, count
2012                let count_expr = self.parse_expression()?;
2013                (
2014                    Some(Limit {
2015                        this: count_expr,
2016                        percent: false,
2017                        comments: Vec::new(),
2018                    }),
2019                    Some(Offset {
2020                        this: first_expr,
2021                        rows: None,
2022                    }),
2023                )
2024            } else {
2025                (
2026                    Some(Limit {
2027                        this: first_expr,
2028                        percent: false,
2029                        comments: Vec::new(),
2030                    }),
2031                    offset,
2032                )
2033            }
2034        } else {
2035            (limit, offset)
2036        };
2037
2038        // Parse FETCH FIRST/NEXT clause
2039        let fetch = if self.match_token(TokenType::Fetch) {
2040            Some(self.parse_fetch()?)
2041        } else {
2042            None
2043        };
2044
2045        // Parse SAMPLE / TABLESAMPLE clause
2046        let sample = self.parse_sample_clause()?;
2047
2048        // Parse FOR UPDATE/SHARE locks or FOR XML (T-SQL)
2049        let (locks, for_xml) = self.parse_locks_and_for_xml()?;
2050
2051        // TSQL: OPTION clause (e.g., OPTION(LABEL = 'foo', HASH JOIN))
2052        let option = if self.check_identifier("OPTION") && self.check_next(TokenType::LParen) {
2053            self.skip(); // consume OPTION
2054            self.skip(); // consume (
2055            let mut content = String::from("OPTION(");
2056            let mut depth = 1;
2057            while !self.is_at_end() && depth > 0 {
2058                let tok = self.advance();
2059                if tok.token_type == TokenType::LParen {
2060                    depth += 1;
2061                } else if tok.token_type == TokenType::RParen {
2062                    depth -= 1;
2063                }
2064                if depth > 0 {
2065                    if tok.token_type == TokenType::String {
2066                        if content.len() > 7 && !content.ends_with('(') && !content.ends_with(' ') {
2067                            content.push(' ');
2068                        }
2069                        content.push('\'');
2070                        content.push_str(&tok.text.replace('\'', "''"));
2071                        content.push('\'');
2072                    } else if tok.token_type == TokenType::Eq {
2073                        content.push_str(" = ");
2074                    } else if tok.token_type == TokenType::Comma {
2075                        content.push_str(", ");
2076                    } else {
2077                        if content.len() > 7 && !content.ends_with('(') && !content.ends_with(' ') {
2078                            content.push(' ');
2079                        }
2080                        content.push_str(&tok.text);
2081                    }
2082                }
2083            }
2084            content.push(')');
2085            Some(content)
2086        } else {
2087            None
2088        };
2089
2090        // ClickHouse: SETTINGS and FORMAT clauses after LIMIT/OFFSET/FETCH
2091        let (settings, format) = if matches!(
2092            self.config.dialect,
2093            Some(crate::dialects::DialectType::ClickHouse)
2094        ) {
2095            let mut settings: Option<Vec<Expression>> = None;
2096            let mut format: Option<Expression> = None;
2097
2098            loop {
2099                if settings.is_none() && self.match_token(TokenType::Settings) {
2100                    let mut settings_exprs = Vec::new();
2101                    loop {
2102                        settings_exprs.push(self.parse_expression()?);
2103                        if !self.match_token(TokenType::Comma) {
2104                            break;
2105                        }
2106                    }
2107                    settings = Some(settings_exprs);
2108                    continue;
2109                }
2110
2111                if format.is_none() && self.match_token(TokenType::Format) {
2112                    // ClickHouse: FORMAT Null is valid (Null is a keyword token, not an identifier)
2113                    let ident = if self.check(TokenType::Null) {
2114                        let text = self.advance().text;
2115                        Identifier::new(text)
2116                    } else {
2117                        self.expect_identifier_or_keyword_with_quoted()?
2118                    };
2119                    format = Some(Expression::Identifier(ident));
2120                    // ClickHouse: FORMAT <name> may be followed by inline data
2121                    // (CSV rows, JSON objects, etc.) — consume to semicolon
2122                    if matches!(
2123                        self.config.dialect,
2124                        Some(crate::dialects::DialectType::ClickHouse)
2125                    ) && !self.is_at_end()
2126                        && !self.check(TokenType::Semicolon)
2127                        && !self.check(TokenType::Settings)
2128                    {
2129                        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
2130                            self.skip();
2131                        }
2132                    }
2133                    continue;
2134                }
2135
2136                break;
2137            }
2138
2139            (settings, format)
2140        } else {
2141            (None, None)
2142        };
2143
2144        let select = Select {
2145            expressions,
2146            from,
2147            joins,
2148            lateral_views,
2149            prewhere,
2150            where_clause,
2151            group_by,
2152            having,
2153            qualify,
2154            order_by,
2155            distribute_by,
2156            cluster_by,
2157            sort_by,
2158            limit,
2159            offset,
2160            limit_by,
2161            fetch,
2162            distinct,
2163            distinct_on,
2164            top,
2165            with: None,
2166            sample,
2167            settings,
2168            format,
2169            windows,
2170            hint,
2171            connect,
2172            into,
2173            locks,
2174            for_xml,
2175            leading_comments,
2176            post_select_comments,
2177            kind,
2178            operation_modifiers,
2179            qualify_after_window,
2180            option,
2181            exclude,
2182        };
2183
2184        Ok(Expression::Select(Box::new(select)))
2185    }
2186
2187    /// Parse a WITH clause (CTEs)
2188    fn parse_with(&mut self) -> Result<Expression> {
2189        use crate::dialects::DialectType;
2190
2191        let with_token = self.expect(TokenType::With)?;
2192        let leading_comments = with_token.comments;
2193
2194        let recursive = self.match_token(TokenType::Recursive);
2195        let mut ctes = Vec::new();
2196
2197        loop {
2198            // ClickHouse supports expression-first WITH items:
2199            // WITH <expr> AS <alias> SELECT ...
2200            if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
2201                let saved_pos = self.current;
2202                if let Ok(expr) = self.parse_expression() {
2203                    // Check if parse_expression already consumed the AS alias
2204                    // (e.g., `(1, 2) AS a` gets parsed as Alias(Tuple, "a") by the tuple alias handler)
2205                    let (inner_expr, alias_opt) = if let Expression::Alias(ref alias_box) = expr {
2206                        (alias_box.this.clone(), Some(alias_box.alias.clone()))
2207                    } else {
2208                        (expr, None)
2209                    };
2210
2211                    if let Some(alias) = alias_opt {
2212                        // Expression already had AS alias consumed
2213                        ctes.push(Cte {
2214                            alias,
2215                            this: inner_expr,
2216                            columns: Vec::new(),
2217                            materialized: None,
2218                            key_expressions: Vec::new(),
2219                            alias_first: false,
2220                            comments: Vec::new(),
2221                        });
2222
2223                        if self.match_token(TokenType::Comma) {
2224                            continue;
2225                        }
2226                        break;
2227                    } else if self.match_token(TokenType::As)
2228                        && self.is_identifier_or_keyword_token()
2229                    {
2230                        // Require AS <alias> to disambiguate from standard CTE syntax
2231                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
2232                        ctes.push(Cte {
2233                            alias,
2234                            this: inner_expr,
2235                            columns: Vec::new(),
2236                            materialized: None,
2237                            key_expressions: Vec::new(),
2238                            alias_first: false,
2239                            comments: Vec::new(),
2240                        });
2241
2242                        if self.match_token(TokenType::Comma) {
2243                            continue;
2244                        }
2245                        break;
2246                    } else if self.check(TokenType::Select) || self.check(TokenType::Comma) {
2247                        // ClickHouse: WITH expr SELECT ... (unaliased expression in CTE)
2248                        ctes.push(Cte {
2249                            alias: Identifier::new(format!("{}", inner_expr)),
2250                            this: inner_expr,
2251                            columns: Vec::new(),
2252                            materialized: None,
2253                            key_expressions: Vec::new(),
2254                            alias_first: false,
2255                            comments: Vec::new(),
2256                        });
2257
2258                        if self.match_token(TokenType::Comma) {
2259                            continue;
2260                        }
2261                        break;
2262                    }
2263                }
2264                // Fall back to standard CTE parsing
2265                self.current = saved_pos;
2266            }
2267
2268            // CTE names can be keywords like 'view', 'use', 'all', etc.
2269            let name = self.expect_identifier_or_alias_keyword_with_quoted()?;
2270
2271            // Optional column list
2272            // But first check for Snowflake-style CTE: WITH t (SELECT ...) - no AS keyword
2273            // In that case, LParen is followed by SELECT, not column names
2274            let columns = if self.check(TokenType::LParen) && !self.check_next(TokenType::Select) {
2275                self.skip(); // consume LParen
2276                let cols = self.parse_identifier_list()?;
2277                self.expect(TokenType::RParen)?;
2278                cols
2279            } else {
2280                Vec::new()
2281            };
2282
2283            // Optional USING KEY (columns) for DuckDB recursive CTEs
2284            let key_expressions = if self.match_keywords(&[TokenType::Using, TokenType::Key]) {
2285                self.expect(TokenType::LParen)?;
2286                let keys = self.parse_identifier_list()?;
2287                self.expect(TokenType::RParen)?;
2288                keys
2289            } else {
2290                Vec::new()
2291            };
2292
2293            // ClickHouse: keyword -> body AS alias (single-param lambda where param is a keyword)
2294            // e.g., WITH time -> sin(time * 2 * pi()) AS sine_wave
2295            if matches!(self.config.dialect, Some(DialectType::ClickHouse))
2296                && self.check(TokenType::Arrow)
2297            {
2298                self.skip(); // consume ->
2299                let body = self.parse_expression()?;
2300                let lambda = Expression::Lambda(Box::new(LambdaExpr {
2301                    parameters: vec![name.clone()],
2302                    body,
2303                    colon: false,
2304                    parameter_types: Vec::new(),
2305                }));
2306                // Expect AS alias
2307                if self.match_token(TokenType::As) && self.is_identifier_or_keyword_token() {
2308                    let alias = self.expect_identifier_or_keyword_with_quoted()?;
2309                    ctes.push(Cte {
2310                        alias,
2311                        this: lambda,
2312                        columns: Vec::new(),
2313                        materialized: None,
2314                        key_expressions: Vec::new(),
2315                        alias_first: false,
2316                        comments: Vec::new(),
2317                    });
2318                } else {
2319                    // Unaliased lambda CTE
2320                    ctes.push(Cte {
2321                        alias: name,
2322                        this: lambda,
2323                        columns: Vec::new(),
2324                        materialized: None,
2325                        key_expressions: Vec::new(),
2326                        alias_first: false,
2327                        comments: Vec::new(),
2328                    });
2329                }
2330                if self.match_token(TokenType::Comma) {
2331                    continue;
2332                }
2333                break;
2334            }
2335
2336            // AS is optional (Snowflake allows WITH t (SELECT ...) without AS)
2337            let cte_comments = if self.match_token(TokenType::As) {
2338                // Capture trailing comments from the AS token
2339                // e.g., "WITH a AS /* comment */ (...)" -> comment goes after alias
2340                self.previous_trailing_comments().to_vec()
2341            } else {
2342                Vec::new()
2343            };
2344
2345            // Check for MATERIALIZED or NOT MATERIALIZED
2346            let materialized = if self.match_token(TokenType::Materialized) {
2347                Some(true)
2348            } else if self.match_token(TokenType::Not) {
2349                self.expect(TokenType::Materialized)?;
2350                Some(false)
2351            } else {
2352                None
2353            };
2354
2355            self.expect(TokenType::LParen)?;
2356            let query = self.parse_statement()?;
2357            self.expect(TokenType::RParen)?;
2358
2359            ctes.push(Cte {
2360                alias: name,
2361                this: query,
2362                columns,
2363                materialized,
2364                key_expressions,
2365                alias_first: true,
2366                comments: cte_comments,
2367            });
2368
2369            if !self.match_token(TokenType::Comma) {
2370                // Check for WITH merging: WITH a AS (...) WITH b AS (...) -> merged
2371                // If the next token is WITH (not followed by nothing), continue parsing CTEs
2372                if self.check(TokenType::With) {
2373                    self.skip(); // consume the redundant WITH keyword
2374                                 // Check if this WITH is also RECURSIVE
2375                    if self.match_token(TokenType::Recursive) && !recursive {
2376                        // If second WITH is RECURSIVE but first wasn't, ignore (keep non-recursive)
2377                    }
2378                    continue; // continue the loop to parse more CTEs
2379                }
2380                break;
2381            }
2382            // WI-14f: Skip redundant WITH keyword after comma in CTE list
2383            // e.g., WITH a AS (SELECT 1), WITH b AS (SELECT 2) SELECT *
2384            self.match_token(TokenType::With);
2385        }
2386
2387        // Parse optional SEARCH/CYCLE clause for recursive CTEs (PostgreSQL)
2388        // Syntax: SEARCH BREADTH|DEPTH FIRST BY column SET column [USING column]
2389        //     or: CYCLE column SET column USING column
2390        let search = self.parse_recursive_with_search()?;
2391
2392        // Parse the main query
2393        let mut main_query = self.parse_statement()?;
2394
2395        // Unwrap parenthesized wrappers to find the inner SELECT
2396        // (matching Python sqlglot: while isinstance(this, Subquery) and this.is_wrapper)
2397        loop {
2398            match main_query {
2399                Expression::Paren(paren) => {
2400                    main_query = paren.this;
2401                }
2402                Expression::Subquery(ref sub)
2403                    if sub.alias.is_none()
2404                        && sub.order_by.is_none()
2405                        && sub.limit.is_none()
2406                        && sub.offset.is_none() =>
2407                {
2408                    // Unwrap Subquery wrapper (parenthesized query without modifiers)
2409                    if let Expression::Subquery(sub) = main_query {
2410                        main_query = sub.this;
2411                    } else {
2412                        break;
2413                    }
2414                }
2415                _ => break,
2416            }
2417        }
2418
2419        // Attach WITH to the main query
2420        let with_clause = With {
2421            ctes,
2422            recursive,
2423            leading_comments,
2424            search,
2425        };
2426        match &mut main_query {
2427            Expression::Select(ref mut select) => {
2428                select.with = Some(with_clause);
2429            }
2430            Expression::Union(ref mut union) => {
2431                union.with = Some(with_clause);
2432            }
2433            Expression::Intersect(ref mut intersect) => {
2434                intersect.with = Some(with_clause);
2435            }
2436            Expression::Except(ref mut except) => {
2437                except.with = Some(with_clause);
2438            }
2439            Expression::Update(ref mut update) => {
2440                update.with = Some(with_clause);
2441            }
2442            Expression::Insert(ref mut insert) => {
2443                insert.with = Some(with_clause);
2444            }
2445            Expression::Delete(ref mut delete) => {
2446                delete.with = Some(with_clause);
2447            }
2448            Expression::CreateTable(ref mut ct) => {
2449                ct.with_cte = Some(with_clause);
2450            }
2451            Expression::Pivot(ref mut pivot) => {
2452                pivot.with = Some(with_clause);
2453            }
2454            _ => {}
2455        }
2456
2457        Ok(main_query)
2458    }
2459
2460    /// Parse SELECT expressions
2461    fn parse_select_expressions(&mut self) -> Result<Vec<Expression>> {
2462        let mut expressions = Vec::new();
2463
2464        loop {
2465            // Check if we're at end of select list (empty list case for TSQL TOP)
2466            // This allows queries like "SELECT TOP 10 PERCENT" with no columns
2467            // Also check for Oracle BULK COLLECT INTO sequence
2468            // ClickHouse: minus() is tokenized as Except but should be treated as function
2469            let is_ch_keyword_func = matches!(
2470                self.config.dialect,
2471                Some(crate::dialects::DialectType::ClickHouse)
2472            ) && (self.check(TokenType::Except)
2473                || self.check(TokenType::Intersect))
2474                && self.check_next(TokenType::LParen);
2475            // ClickHouse: `from`/`except` can be column names when followed by an operator
2476            // (e.g., `from + from`, `from in [0]`, `from, ...`)
2477            // Also: `from FROM t` — two consecutive FROM tokens means first is column name
2478            let is_ch_keyword_as_column = matches!(
2479                self.config.dialect,
2480                Some(crate::dialects::DialectType::ClickHouse)
2481            ) && (self.check(TokenType::From)
2482                || self.check(TokenType::Except))
2483                && {
2484                    let next_tt = self
2485                        .peek_nth(1)
2486                        .map(|t| t.token_type)
2487                        .unwrap_or(TokenType::Semicolon);
2488                    matches!(
2489                        next_tt,
2490                        TokenType::Plus | TokenType::Dash | TokenType::Star | TokenType::Slash
2491                        | TokenType::Percent | TokenType::Eq | TokenType::Neq | TokenType::Lt
2492                        | TokenType::Gt | TokenType::Lte | TokenType::Gte
2493                        | TokenType::And | TokenType::Or | TokenType::Comma | TokenType::Dot
2494                        | TokenType::In | TokenType::Is | TokenType::Not | TokenType::Like
2495                        | TokenType::Between | TokenType::Semicolon | TokenType::RParen
2496                        | TokenType::As | TokenType::DPipe | TokenType::Amp | TokenType::Pipe
2497                        | TokenType::LBracket
2498                        // Two consecutive FROM tokens: first is column name (e.g., SELECT from FROM t)
2499                        | TokenType::From
2500                    )
2501                };
2502            if !is_ch_keyword_func
2503                && !is_ch_keyword_as_column
2504                && (self.is_at_end()
2505                    || self.check(TokenType::From)
2506                    || self.check(TokenType::Where)
2507                    || self.check(TokenType::Into)
2508                    || self.check(TokenType::Union)
2509                    || self.check(TokenType::Intersect)
2510                    || self.check(TokenType::Except)
2511                    || self.check(TokenType::Order)
2512                    || self.check(TokenType::Limit)
2513                    || self.check(TokenType::Semicolon)
2514                    || self.check_text_seq(&["BULK", "COLLECT", "INTO"]))
2515            {
2516                break;
2517            }
2518
2519            // Handle star
2520            if self.check(TokenType::Star) {
2521                self.skip();
2522                let star_trailing_comments = self.previous_trailing_comments().to_vec();
2523                let star = self.parse_star_modifiers_with_comments(None, star_trailing_comments)?;
2524                let mut star_expr = Expression::Star(star);
2525                // ClickHouse: * APPLY(func) or * APPLY func or * APPLY(x -> expr) column transformer
2526                if matches!(
2527                    self.config.dialect,
2528                    Some(crate::dialects::DialectType::ClickHouse)
2529                ) {
2530                    while self.check(TokenType::Apply) {
2531                        self.skip(); // consume APPLY
2532                        let apply_expr = if self.match_token(TokenType::LParen) {
2533                            // Could be APPLY(func_name) or APPLY(x -> expr)
2534                            let expr = self.parse_expression()?;
2535                            self.expect(TokenType::RParen)?;
2536                            expr
2537                        } else {
2538                            // APPLY func or APPLY x -> expr (no parens)
2539                            // Parse as expression to handle lambdas
2540                            self.parse_expression()?
2541                        };
2542                        star_expr = Expression::Apply(Box::new(crate::expressions::Apply {
2543                            this: Box::new(star_expr),
2544                            expression: Box::new(apply_expr),
2545                        }));
2546                    }
2547                }
2548                // ClickHouse: Also handle EXCEPT/REPLACE between APPLYs:
2549                // * APPLY(toDate) EXCEPT(i, j) APPLY(any)
2550                if matches!(
2551                    self.config.dialect,
2552                    Some(crate::dialects::DialectType::ClickHouse)
2553                ) && (self.check(TokenType::Except)
2554                    || self.check(TokenType::Exclude)
2555                    || self.check(TokenType::Replace))
2556                {
2557                    // Consume EXCEPT/REPLACE modifiers after APPLY
2558                    self.parse_star_modifiers(None)?;
2559                    // Continue with more APPLYs
2560                    while self.check(TokenType::Apply) {
2561                        self.skip();
2562                        let apply_expr = if self.match_token(TokenType::LParen) {
2563                            let expr = self.parse_expression()?;
2564                            self.expect(TokenType::RParen)?;
2565                            expr
2566                        } else {
2567                            self.parse_expression()?
2568                        };
2569                        star_expr = Expression::Apply(Box::new(crate::expressions::Apply {
2570                            this: Box::new(star_expr),
2571                            expression: Box::new(apply_expr),
2572                        }));
2573                    }
2574                }
2575                // ClickHouse: * followed by operators (e.g., * IS NOT NULL, * AND expr)
2576                // Treat * as a regular expression and continue parsing operators
2577                if matches!(
2578                    self.config.dialect,
2579                    Some(crate::dialects::DialectType::ClickHouse)
2580                ) && matches!(
2581                    self.peek().token_type,
2582                    TokenType::Is
2583                        | TokenType::And
2584                        | TokenType::Or
2585                        | TokenType::Eq
2586                        | TokenType::Neq
2587                        | TokenType::Lt
2588                        | TokenType::Gt
2589                        | TokenType::Lte
2590                        | TokenType::Gte
2591                        | TokenType::Not
2592                        | TokenType::Plus
2593                        | TokenType::Dash
2594                        | TokenType::Slash
2595                        | TokenType::Percent
2596                        | TokenType::Like
2597                        | TokenType::Between
2598                        | TokenType::In
2599                ) {
2600                    // Re-parse from the operator with star_expr as the left side
2601                    let left = star_expr;
2602                    // Use parse_comparison / parse_is chain
2603                    if self.check(TokenType::Is) {
2604                        self.skip(); // consume IS
2605                        let not = self.match_token(TokenType::Not);
2606                        if self.match_token(TokenType::Null) {
2607                            star_expr = if not {
2608                                Expression::Not(Box::new(UnaryOp {
2609                                    this: Expression::Is(Box::new(BinaryOp::new(
2610                                        left,
2611                                        Expression::Null(Null),
2612                                    ))),
2613                                    inferred_type: None,
2614                                }))
2615                            } else {
2616                                Expression::Is(Box::new(BinaryOp::new(
2617                                    left,
2618                                    Expression::Null(Null),
2619                                )))
2620                            };
2621                        } else {
2622                            let right = self.parse_or()?;
2623                            star_expr = if not {
2624                                Expression::Not(Box::new(UnaryOp {
2625                                    this: Expression::Is(Box::new(BinaryOp::new(left, right))),
2626                                    inferred_type: None,
2627                                }))
2628                            } else {
2629                                Expression::Is(Box::new(BinaryOp::new(left, right)))
2630                            };
2631                        }
2632                    } else if self.match_token(TokenType::And) {
2633                        let right = self.parse_or()?;
2634                        star_expr = Expression::And(Box::new(BinaryOp::new(left, right)));
2635                    } else if self.match_token(TokenType::Or) {
2636                        let right = self.parse_or()?;
2637                        star_expr = Expression::Or(Box::new(BinaryOp::new(left, right)));
2638                    } else {
2639                        let op_token = self.advance();
2640                        let right = self.parse_or()?;
2641                        star_expr = match op_token.token_type {
2642                            TokenType::Eq => Expression::Eq(Box::new(BinaryOp::new(left, right))),
2643                            TokenType::Neq => Expression::Neq(Box::new(BinaryOp::new(left, right))),
2644                            TokenType::Lt => Expression::Lt(Box::new(BinaryOp::new(left, right))),
2645                            TokenType::Gt => Expression::Gt(Box::new(BinaryOp::new(left, right))),
2646                            TokenType::Lte => Expression::Lte(Box::new(BinaryOp::new(left, right))),
2647                            TokenType::Gte => Expression::Gte(Box::new(BinaryOp::new(left, right))),
2648                            TokenType::Plus => {
2649                                Expression::Add(Box::new(BinaryOp::new(left, right)))
2650                            }
2651                            TokenType::Dash => {
2652                                Expression::Sub(Box::new(BinaryOp::new(left, right)))
2653                            }
2654                            _ => left, // fallback
2655                        };
2656                    }
2657                }
2658                expressions.push(star_expr);
2659            } else {
2660                // Capture leading comments from the first token before parsing
2661                // These are comments on a separate line before the expression
2662                let leading_comments = self.current_leading_comments().to_vec();
2663                let expr = self.parse_expression()?;
2664
2665                // ClickHouse: COLUMNS(id, value) EXCEPT (id) REPLACE (5 AS id) APPLY func
2666                // Also: a.* APPLY(toDate) EXCEPT(i, j) APPLY(any) - qualified star with APPLY
2667                let expr = if matches!(
2668                    self.config.dialect,
2669                    Some(crate::dialects::DialectType::ClickHouse)
2670                ) {
2671                    let is_columns_func = match &expr {
2672                        Expression::Function(f) => f.name.eq_ignore_ascii_case("COLUMNS"),
2673                        Expression::MethodCall(m) => m.method.name.eq_ignore_ascii_case("COLUMNS"),
2674                        Expression::Columns(_) => true,
2675                        _ => false,
2676                    };
2677                    let is_qualified_star = matches!(&expr, Expression::Star(_));
2678                    if (is_columns_func || is_qualified_star)
2679                        && (self.check(TokenType::Except)
2680                            || self.check(TokenType::Exclude)
2681                            || self.check(TokenType::Replace)
2682                            || self.check(TokenType::Apply))
2683                    {
2684                        let mut result = expr;
2685                        // Parse any mix of EXCEPT/REPLACE/APPLY in any order
2686                        // e.g., * APPLY(toDate) EXCEPT(i, j) APPLY(any)
2687                        loop {
2688                            if self.check(TokenType::Except) || self.check(TokenType::Exclude) {
2689                                // Parse EXCEPT/EXCLUDE modifier
2690                                self.skip();
2691                                self.match_identifier("STRICT");
2692                                if self.match_token(TokenType::LParen) {
2693                                    loop {
2694                                        if self.check(TokenType::RParen) {
2695                                            break;
2696                                        }
2697                                        let _ = self.parse_expression()?;
2698                                        if !self.match_token(TokenType::Comma) {
2699                                            break;
2700                                        }
2701                                    }
2702                                    self.expect(TokenType::RParen)?;
2703                                } else if self.is_identifier_token()
2704                                    || self.is_safe_keyword_as_identifier()
2705                                {
2706                                    let _ = self.parse_expression()?;
2707                                }
2708                            } else if self.check(TokenType::Replace) {
2709                                // Parse REPLACE modifier: REPLACE (expr AS alias, ...)
2710                                self.skip();
2711                                self.match_identifier("STRICT");
2712                                if self.match_token(TokenType::LParen) {
2713                                    loop {
2714                                        if self.check(TokenType::RParen) {
2715                                            break;
2716                                        }
2717                                        let _ = self.parse_expression()?;
2718                                        if self.match_token(TokenType::As) {
2719                                            if self.is_identifier_token()
2720                                                || self.is_safe_keyword_as_identifier()
2721                                            {
2722                                                self.skip();
2723                                            }
2724                                        }
2725                                        if !self.match_token(TokenType::Comma) {
2726                                            break;
2727                                        }
2728                                    }
2729                                    self.expect(TokenType::RParen)?;
2730                                } else {
2731                                    let _ = self.parse_expression()?;
2732                                    if self.match_token(TokenType::As) {
2733                                        if self.is_identifier_token()
2734                                            || self.is_safe_keyword_as_identifier()
2735                                        {
2736                                            self.skip();
2737                                        }
2738                                    }
2739                                }
2740                            } else if self.check(TokenType::Apply) {
2741                                // Parse APPLY transformer
2742                                self.skip();
2743                                let apply_expr = if self.match_token(TokenType::LParen) {
2744                                    let e = self.parse_expression()?;
2745                                    self.expect(TokenType::RParen)?;
2746                                    e
2747                                } else {
2748                                    self.parse_expression()?
2749                                };
2750                                result = Expression::Apply(Box::new(crate::expressions::Apply {
2751                                    this: Box::new(result),
2752                                    expression: Box::new(apply_expr),
2753                                }));
2754                            } else {
2755                                break;
2756                            }
2757                        }
2758                        result
2759                    } else {
2760                        expr
2761                    }
2762                } else {
2763                    expr
2764                };
2765
2766                // Capture comments between expression and potential AS
2767                let pre_alias_comments = self.previous_trailing_comments().to_vec();
2768
2769                // DuckDB prefix alias syntax: identifier: expression (e.g., "foo: 1" means "1 AS foo")
2770                // Check if the expression is a simple identifier followed by a colon
2771                let expr = if self.check(TokenType::Colon) && !self.check_next(TokenType::Colon) {
2772                    // Extract the alias name from the identifier expression
2773                    let alias_ident = match &expr {
2774                        Expression::Identifier(id) => Some(id.clone()),
2775                        Expression::Column(col) if col.table.is_none() => Some(col.name.clone()),
2776                        _ => None,
2777                    };
2778                    if let Some(alias) = alias_ident {
2779                        // Consume the colon
2780                        self.skip();
2781                        let colon_comments = self.previous_trailing_comments().to_vec();
2782                        // Parse the actual value expression
2783                        let value = self.parse_expression()?;
2784                        let value_trailing = self.previous_trailing_comments().to_vec();
2785                        // For colon-alias (foo: expr), comments between alias and colon should
2786                        // become trailing comments (placed after the alias in output).
2787                        // Comments after the value expression are also trailing.
2788                        let mut all_trailing = pre_alias_comments.clone();
2789                        all_trailing.extend(colon_comments);
2790                        all_trailing.extend(value_trailing);
2791                        Expression::Alias(Box::new(Alias {
2792                            this: value,
2793                            alias,
2794                            column_aliases: Vec::new(),
2795                            pre_alias_comments: Vec::new(),
2796                            trailing_comments: all_trailing,
2797                            inferred_type: None,
2798                        }))
2799                    } else {
2800                        // Not a simple identifier, fall through to normal alias handling
2801                        // (this handles cases where the expression is complex before the colon)
2802                        expr
2803                    }
2804                } else if self.match_token(TokenType::As) {
2805                    // Capture comments from AS token (e.g., AS /* foo */ (a, b, c))
2806                    // These go into trailing_comments (after the alias), not pre_alias_comments
2807                    let as_comments = self.previous_trailing_comments().to_vec();
2808                    // Check for column aliases: AS (col1, col2) - used by POSEXPLODE etc.
2809                    if self.match_token(TokenType::LParen) {
2810                        let mut column_aliases = Vec::new();
2811                        loop {
2812                            if let Some(col_expr) = self.parse_id_var()? {
2813                                if let Expression::Identifier(id) = col_expr {
2814                                    column_aliases.push(id);
2815                                }
2816                            } else {
2817                                break;
2818                            }
2819                            if !self.match_token(TokenType::Comma) {
2820                                break;
2821                            }
2822                        }
2823                        self.match_token(TokenType::RParen);
2824                        let mut trailing_comments = as_comments;
2825                        trailing_comments.extend_from_slice(self.previous_trailing_comments());
2826                        Expression::Alias(Box::new(Alias {
2827                            this: expr,
2828                            alias: Identifier::new(String::new()),
2829                            column_aliases,
2830                            pre_alias_comments,
2831                            trailing_comments,
2832                            inferred_type: None,
2833                        }))
2834                    } else {
2835                        // Allow keywords as aliases (e.g., SELECT 1 AS filter)
2836                        // Use _with_quoted to preserve quoted alias
2837                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
2838                        let mut trailing_comments = self.previous_trailing_comments().to_vec();
2839                        // If parse_comparison stored pending leading comments (no comparison
2840                        // followed), use those. Otherwise use the leading_comments we captured
2841                        // before parse_expression(). Both come from the same token, so we
2842                        // only add one set to avoid duplication.
2843                        if !self.pending_leading_comments.is_empty() {
2844                            trailing_comments.extend(self.pending_leading_comments.drain(..));
2845                        } else {
2846                            trailing_comments.extend(leading_comments.iter().cloned());
2847                        }
2848                        Expression::Alias(Box::new(Alias {
2849                            this: expr,
2850                            alias,
2851                            column_aliases: Vec::new(),
2852                            pre_alias_comments,
2853                            trailing_comments,
2854                            inferred_type: None,
2855                        }))
2856                    }
2857                } 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)
2858                    // ClickHouse: APPLY without ( is an implicit alias (e.g., SELECT col apply)
2859                    || (self.check(TokenType::Apply) && !self.check_next(TokenType::LParen)
2860                        && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))))
2861                    && !self.check_text_seq(&["BULK", "COLLECT", "INTO"])
2862                    // ClickHouse clauses must not be consumed as implicit aliases.
2863                    && !(matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
2864                        && (self.check(TokenType::Format) || self.check(TokenType::Settings)))
2865                    // LIMIT/OFFSET/FETCH are clause starters in most dialects and must not
2866                    // be consumed as implicit aliases in SELECT lists.
2867                    && !(
2868                        self.check(TokenType::Fetch)
2869                        || ((self.check(TokenType::Limit) || self.check(TokenType::Offset))
2870                            && !matches!(
2871                                self.config.dialect,
2872                                Some(crate::dialects::DialectType::Spark)
2873                                    | Some(crate::dialects::DialectType::Hive)
2874                            ))
2875                    )
2876                    // GROUP BY / ORDER BY are clause boundaries, not aliases.
2877                    && !self.check_text_seq(&["GROUP", "BY"])
2878                    && !self.check_text_seq(&["ORDER", "BY"])
2879                    // WINDOW is a clause boundary (named window definitions), not an alias.
2880                    && !self.check(TokenType::Window)
2881                    // ClickHouse: PARALLEL WITH is a statement separator, not an alias.
2882                    && !(self.check_identifier("PARALLEL") && self.check_next(TokenType::With)
2883                        && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
2884                {
2885                    // Implicit alias (without AS) - allow Var tokens, QuotedIdentifiers, command keywords (like GET, PUT, etc.), and OVERLAPS
2886                    // But NOT when it's the Oracle BULK COLLECT INTO sequence
2887                    let alias_token = self.advance();
2888                    let alias_text = alias_token.text.clone();
2889                    let is_quoted = alias_token.token_type == TokenType::QuotedIdentifier;
2890                    let trailing_comments = self.previous_trailing_comments().to_vec();
2891                    Expression::Alias(Box::new(Alias {
2892                        this: expr,
2893                        alias: Identifier {
2894                            name: alias_text,
2895                            quoted: is_quoted,
2896                            trailing_comments: Vec::new(),
2897                            span: None,
2898                        },
2899                        column_aliases: Vec::new(),
2900                        pre_alias_comments,
2901                        trailing_comments,
2902                        inferred_type: None,
2903                    }))
2904                } else if !pre_alias_comments.is_empty() {
2905                    // Only wrap in Annotated if the expression doesn't already handle trailing comments.
2906                    // BinaryOp, Column, Cast, Function, etc. have their own trailing_comments field that the generator uses.
2907                    let already_has_trailing = matches!(
2908                        &expr,
2909                        Expression::Add(_)
2910                            | Expression::Sub(_)
2911                            | Expression::Mul(_)
2912                            | Expression::Div(_)
2913                            | Expression::Mod(_)
2914                            | Expression::Concat(_)
2915                            | Expression::BitwiseAnd(_)
2916                            | Expression::BitwiseOr(_)
2917                            | Expression::BitwiseXor(_)
2918                            | Expression::Column(_)
2919                            | Expression::Paren(_)
2920                            | Expression::Annotated(_)
2921                            | Expression::Cast(_)
2922                            | Expression::Function(_)
2923                            | Expression::Subquery(_)
2924                    );
2925                    if already_has_trailing {
2926                        expr
2927                    } else {
2928                        // Wrap in Annotated to preserve trailing comments
2929                        Expression::Annotated(Box::new(Annotated {
2930                            this: expr,
2931                            trailing_comments: pre_alias_comments,
2932                        }))
2933                    }
2934                } else if !leading_comments.is_empty() {
2935                    // Wrap in Annotated to preserve leading comments as trailing comments
2936                    Expression::Annotated(Box::new(Annotated {
2937                        this: expr,
2938                        trailing_comments: leading_comments,
2939                    }))
2940                } else {
2941                    expr
2942                };
2943
2944                expressions.push(expr);
2945            }
2946
2947            if !self.match_token(TokenType::Comma) {
2948                break;
2949            }
2950
2951            // Handle trailing comma (ClickHouse supports trailing commas in SELECT)
2952            // ClickHouse: `from` after comma is a column name if followed by an operator
2953            // (e.g., `from + from` or `from in [0]`), comma, or line-end
2954            let from_is_column = matches!(
2955                self.config.dialect,
2956                Some(crate::dialects::DialectType::ClickHouse)
2957            ) && self.check(TokenType::From)
2958                && {
2959                    let next_tt = self
2960                        .peek_nth(1)
2961                        .map(|t| t.token_type)
2962                        .unwrap_or(TokenType::Semicolon);
2963                    matches!(
2964                        next_tt,
2965                        TokenType::Plus
2966                            | TokenType::Dash
2967                            | TokenType::Star
2968                            | TokenType::Slash
2969                            | TokenType::Percent
2970                            | TokenType::Eq
2971                            | TokenType::Neq
2972                            | TokenType::Lt
2973                            | TokenType::Gt
2974                            | TokenType::Lte
2975                            | TokenType::Gte
2976                            | TokenType::And
2977                            | TokenType::Or
2978                            | TokenType::Comma
2979                            | TokenType::Dot
2980                            | TokenType::In
2981                            | TokenType::Is
2982                            | TokenType::Not
2983                            | TokenType::Like
2984                            | TokenType::Between
2985                            | TokenType::Semicolon
2986                            | TokenType::RParen
2987                            | TokenType::As
2988                            | TokenType::DPipe
2989                            | TokenType::Amp
2990                            | TokenType::Pipe
2991                            | TokenType::LBracket
2992                    )
2993                };
2994            if (self.config.allow_trailing_commas
2995                || matches!(
2996                    self.config.dialect,
2997                    Some(crate::dialects::DialectType::ClickHouse)
2998                ))
2999                && (!from_is_column && self.check_from_keyword()
3000                    || self.check(TokenType::Where)
3001                    || self.check(TokenType::GroupBy)
3002                    || self.check(TokenType::Having)
3003                    || self.check(TokenType::Order)
3004                    || self.check(TokenType::Limit)
3005                    || self.check(TokenType::Union)
3006                    || self.check(TokenType::Intersect)
3007                    || (self.check(TokenType::Except) && !self.check_next(TokenType::LParen) && !self.check_next(TokenType::Comma))
3008                    || self.check(TokenType::Semicolon)
3009                    || self.check(TokenType::RParen)
3010                    // SETTINGS/FORMAT only as boundaries when NOT followed by ( or [ (function/column ref)
3011                    || (self.check(TokenType::Settings) && !self.check_next(TokenType::LParen) && !self.check_next(TokenType::LBracket))
3012                    || (self.check(TokenType::Format) && !self.check_next(TokenType::LParen))
3013                    || self.is_at_end())
3014            {
3015                break;
3016            }
3017        }
3018
3019        Ok(expressions)
3020    }
3021
3022    /// Parse DuckDB FROM-first query syntax
3023    /// FROM tbl = SELECT * FROM tbl
3024    /// FROM tbl SELECT col1, col2 = SELECT col1, col2 FROM tbl
3025    fn parse_from_first_query(&mut self) -> Result<Expression> {
3026        self.expect(TokenType::From)?;
3027
3028        // Parse the FROM clause (table references)
3029        let from = self.parse_from()?;
3030
3031        // Check if there's an explicit SELECT clause after FROM
3032        let expressions = if self.check(TokenType::Select) {
3033            self.skip(); // consume SELECT
3034            self.parse_select_expressions()?
3035        } else {
3036            // No explicit SELECT means SELECT *
3037            vec![Expression::Star(crate::expressions::Star {
3038                table: None,
3039                except: None,
3040                replace: None,
3041                rename: None,
3042                trailing_comments: Vec::new(),
3043                span: None,
3044            })]
3045        };
3046
3047        // Parse PREWHERE clause (ClickHouse specific)
3048        let prewhere = if self.match_token(TokenType::Prewhere) {
3049            Some(self.parse_expression()?)
3050        } else {
3051            None
3052        };
3053
3054        // Parse WHERE clause
3055        let where_clause = if self.match_token(TokenType::Where) {
3056            Some(Where {
3057                this: self.parse_expression()?,
3058            })
3059        } else {
3060            None
3061        };
3062
3063        // Parse GROUP BY
3064        let group_by = if self.match_token(TokenType::Group) {
3065            self.expect(TokenType::By)?;
3066            let mut groups = Vec::new();
3067            loop {
3068                groups.push(self.parse_expression()?);
3069                if !self.match_token(TokenType::Comma) {
3070                    break;
3071                }
3072            }
3073            Some(GroupBy {
3074                expressions: groups,
3075                all: None,
3076                totals: false,
3077                comments: Vec::new(),
3078            })
3079        } else {
3080            None
3081        };
3082
3083        // Parse HAVING
3084        let having = if self.match_token(TokenType::Having) {
3085            Some(Having {
3086                this: self.parse_expression()?,
3087                comments: Vec::new(),
3088            })
3089        } else {
3090            None
3091        };
3092
3093        // Parse ORDER BY
3094        let order_by = if self.match_token(TokenType::Order) {
3095            self.expect(TokenType::By)?;
3096            Some(self.parse_order_by()?)
3097        } else {
3098            None
3099        };
3100
3101        // Parse LIMIT
3102        let limit = if self.match_token(TokenType::Limit) {
3103            let first_expr = self.parse_expression()?;
3104            Some(Limit {
3105                this: first_expr,
3106                percent: false,
3107                comments: Vec::new(),
3108            })
3109        } else {
3110            None
3111        };
3112
3113        // Parse OFFSET
3114        let offset = if self.match_token(TokenType::Offset) {
3115            let expr = self.parse_expression()?;
3116            let rows = if self.match_token(TokenType::Row) || self.match_token(TokenType::Rows) {
3117                Some(true)
3118            } else {
3119                None
3120            };
3121            Some(Offset { this: expr, rows })
3122        } else {
3123            None
3124        };
3125
3126        // Build SELECT expression
3127        let select = Select {
3128            expressions,
3129            from: Some(from),
3130            joins: Vec::new(),
3131            lateral_views: Vec::new(),
3132            prewhere,
3133            where_clause,
3134            group_by,
3135            having,
3136            qualify: None,
3137            order_by,
3138            distribute_by: None,
3139            cluster_by: None,
3140            sort_by: None,
3141            limit,
3142            offset,
3143            limit_by: None,
3144            fetch: None,
3145            distinct: false,
3146            distinct_on: None,
3147            top: None,
3148            with: None,
3149            sample: None,
3150            settings: None,
3151            format: None,
3152            windows: None,
3153            hint: None,
3154            connect: None,
3155            into: None,
3156            locks: Vec::new(),
3157            for_xml: Vec::new(),
3158            leading_comments: Vec::new(),
3159            post_select_comments: Vec::new(),
3160            kind: None,
3161            operation_modifiers: Vec::new(),
3162            qualify_after_window: false,
3163            option: None,
3164            exclude: None,
3165        };
3166
3167        // Check for set operations (UNION, INTERSECT, EXCEPT)
3168        let result = Expression::Select(Box::new(select));
3169        self.parse_set_operation(result)
3170    }
3171
3172    /// Parse FROM clause
3173    fn parse_from(&mut self) -> Result<From> {
3174        let mut expressions = Vec::new();
3175
3176        loop {
3177            // Capture leading comments before each table expression
3178            // (e.g., FROM \n/* comment */\n table_name)
3179            let pre_table_comments = if !self.is_at_end() {
3180                self.tokens[self.current].comments.clone()
3181            } else {
3182                Vec::new()
3183            };
3184            // Clear them from the token to avoid double output
3185            if !pre_table_comments.is_empty() && !self.is_at_end() {
3186                self.tokens[self.current].comments.clear();
3187            }
3188
3189            let mut table = self.parse_table_expression()?;
3190
3191            // Attach captured comments as trailing on the outermost expression
3192            if !pre_table_comments.is_empty() {
3193                match &mut table {
3194                    Expression::Pivot(p) => {
3195                        // For PIVOT, find the inner table and add to its leading_comments
3196                        // The generator will output these after the PIVOT clause
3197                        if let Expression::Table(ref mut t) = p.this {
3198                            t.leading_comments = pre_table_comments;
3199                        }
3200                    }
3201                    Expression::Table(ref mut t) => {
3202                        t.trailing_comments.extend(pre_table_comments);
3203                    }
3204                    _ => {}
3205                }
3206            }
3207            expressions.push(table);
3208
3209            if !self.match_token(TokenType::Comma) {
3210                break;
3211            }
3212
3213            // Handle trailing comma in FROM clause (Snowflake allows this)
3214            // If next token is a clause boundary keyword or end of input, break
3215            // Note: For Redshift, UNPIVOT after comma is a table expression (SUPER object traversal),
3216            // so we don't treat it as a boundary in that case
3217            let is_redshift = matches!(
3218                self.config.dialect,
3219                Some(crate::dialects::DialectType::Redshift)
3220            );
3221            let is_unpivot_boundary = !is_redshift && self.check(TokenType::Unpivot);
3222            if self.is_at_end()
3223                || is_unpivot_boundary
3224                || matches!(
3225                    self.peek().token_type,
3226                    TokenType::Where
3227                        | TokenType::GroupBy
3228                        | TokenType::Having
3229                        | TokenType::Order
3230                        | TokenType::Limit
3231                        | TokenType::Offset
3232                        | TokenType::Union
3233                        | TokenType::Intersect
3234                        | TokenType::Except
3235                        | TokenType::Semicolon
3236                        | TokenType::RParen
3237                        | TokenType::Window
3238                        | TokenType::Qualify
3239                        | TokenType::Distribute
3240                        | TokenType::Cluster
3241                        | TokenType::Pivot
3242                )
3243            {
3244                break;
3245            }
3246        }
3247
3248        Ok(From { expressions })
3249    }
3250
3251    /// Parse a table expression (table name, subquery, etc.)
3252    fn parse_table_expression(&mut self) -> Result<Expression> {
3253        // Handle PostgreSQL ONLY modifier: FROM ONLY t1
3254        // ONLY prevents scanning child tables in inheritance hierarchy
3255        let has_only = self.match_token(TokenType::Only);
3256
3257        // Handle PostgreSQL ROWS FROM syntax:
3258        // ROWS FROM (func1(args) AS alias1(col1 type1), func2(args) AS alias2(col2 type2)) [WITH ORDINALITY] [AS alias(cols)]
3259        if self.match_text_seq(&["ROWS", "FROM"]) {
3260            return self.parse_rows_from();
3261        }
3262
3263        // Redshift UNPIVOT in FROM clause for SUPER object traversal:
3264        // UNPIVOT expr [AS val_alias AT attr_alias]
3265        // Examples:
3266        //   UNPIVOT c.c_orders[0]
3267        //   UNPIVOT c.c_orders AS val AT attr
3268        if self.match_token(TokenType::Unpivot) {
3269            return self.parse_redshift_unpivot_table();
3270        }
3271
3272        let mut expr = if self.check(TokenType::Values) && self.check_next(TokenType::LParen) {
3273            // VALUES as table expression: FROM (VALUES ...)
3274            // In ClickHouse, bare `values` without ( is a table name
3275            self.parse_values()?
3276        } else if self.check(TokenType::Values)
3277            && matches!(
3278                self.config.dialect,
3279                Some(crate::dialects::DialectType::ClickHouse)
3280            )
3281        {
3282            // ClickHouse: `values` as a table name (not followed by LParen)
3283            let token = self.advance();
3284            let ident = Identifier::new(token.text);
3285            let trailing_comments = self.previous_trailing_comments().to_vec();
3286            Expression::boxed_table(TableRef {
3287                name: ident,
3288                schema: None,
3289                catalog: None,
3290                alias: None,
3291                alias_explicit_as: false,
3292                column_aliases: Vec::new(),
3293                leading_comments: Vec::new(),
3294                trailing_comments,
3295                when: None,
3296                only: false,
3297                final_: false,
3298                table_sample: None,
3299                hints: Vec::new(),
3300                system_time: None,
3301                partitions: Vec::new(),
3302                identifier_func: None,
3303                changes: None,
3304                version: None,
3305                span: None,
3306            })
3307        } else if self.check(TokenType::DAt) {
3308            // Snowflake stage reference: @stage_name or @"stage_name" or @namespace.stage/path
3309            self.parse_stage_reference()?
3310        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
3311            // Snowflake stage reference tokenized as Var: @mystage/path
3312            // When @ is followed by alphanumeric, tokenizer creates a Var token instead of DAt
3313            self.parse_stage_reference_from_var()?
3314        } else if self.check(TokenType::String) && self.peek().text.starts_with('@') {
3315            // Snowflake stage reference in string: '@mystage' or '@external/location'
3316            self.parse_stage_reference_from_string()?
3317        } else if self.match_token(TokenType::Lateral) {
3318            if self.check(TokenType::LParen) {
3319                // LATERAL (SELECT ...) or LATERAL (table_expression) or LATERAL (FROM ...) for DuckDB
3320                self.expect(TokenType::LParen)?;
3321                if self.check(TokenType::Select)
3322                    || self.check(TokenType::With)
3323                    || self.check(TokenType::From)
3324                {
3325                    let query = self.parse_statement()?;
3326                    self.expect(TokenType::RParen)?;
3327                    Expression::Subquery(Box::new(Subquery {
3328                        this: query,
3329                        alias: None,
3330                        column_aliases: Vec::new(),
3331                        order_by: None,
3332                        limit: None,
3333                        offset: None,
3334                        lateral: true,
3335                        modifiers_inside: false,
3336                        trailing_comments: Vec::new(),
3337                        distribute_by: None,
3338                        sort_by: None,
3339                        cluster_by: None,
3340                        inferred_type: None,
3341                    }))
3342                } else {
3343                    // LATERAL (table_function()) - parenthesized non-subquery
3344                    let table_expr = self.parse_table_expression()?;
3345                    self.expect(TokenType::RParen)?;
3346                    Expression::Subquery(Box::new(Subquery {
3347                        this: table_expr,
3348                        alias: None,
3349                        column_aliases: Vec::new(),
3350                        order_by: None,
3351                        limit: None,
3352                        offset: None,
3353                        lateral: true,
3354                        modifiers_inside: false,
3355                        trailing_comments: Vec::new(),
3356                        distribute_by: None,
3357                        sort_by: None,
3358                        cluster_by: None,
3359                        inferred_type: None,
3360                    }))
3361                }
3362            } else {
3363                // LATERAL function_name(args) [WITH ORDINALITY] [AS alias(columns)]
3364                // Parse function name
3365                let first_ident = self.expect_identifier_or_keyword_with_quoted()?;
3366                let first_name = first_ident.name.clone();
3367
3368                // Parse function arguments
3369                self.expect(TokenType::LParen)?;
3370                let args = if self.check(TokenType::RParen) {
3371                    Vec::new()
3372                } else {
3373                    self.parse_function_arguments()?
3374                };
3375                self.expect(TokenType::RParen)?;
3376
3377                // Handle UNNEST specially to create UnnestFunc expression
3378                let mut func_expr = if first_name.eq_ignore_ascii_case("UNNEST") {
3379                    let mut args_iter = args.into_iter();
3380                    let this = args_iter
3381                        .next()
3382                        .ok_or_else(|| self.parse_error("Expected expression in UNNEST"))?;
3383                    let expressions: Vec<Expression> = args_iter.collect();
3384                    Expression::Unnest(Box::new(crate::expressions::UnnestFunc {
3385                        this,
3386                        expressions,
3387                        with_ordinality: false,
3388                        alias: None,
3389                        offset_alias: None,
3390                    }))
3391                } else {
3392                    Expression::Function(Box::new(Function {
3393                        name: first_name,
3394                        args,
3395                        distinct: false,
3396                        trailing_comments: Vec::new(),
3397                        use_bracket_syntax: false,
3398                        no_parens: false,
3399                        quoted: false,
3400                        span: None,
3401                        inferred_type: None,
3402                    }))
3403                };
3404
3405                // Check for WITH ORDINALITY (Presto) or WITH OFFSET (BigQuery)
3406                let mut with_offset_alias: Option<crate::expressions::Identifier> = None;
3407                let ordinality = if self.match_token(TokenType::With) {
3408                    if self.match_token(TokenType::Ordinality) {
3409                        Some(Box::new(Expression::Boolean(BooleanLiteral {
3410                            value: true,
3411                        })))
3412                    } else if self.check(TokenType::Offset) || self.check_identifier("OFFSET") {
3413                        // BigQuery: WITH OFFSET [AS alias]
3414                        self.skip(); // consume OFFSET
3415                                     // Check for optional offset alias: WITH OFFSET AS y or WITH OFFSET y
3416                        if matches!(
3417                            self.config.dialect,
3418                            Some(crate::dialects::DialectType::BigQuery)
3419                        ) {
3420                            let has_as = self.match_token(TokenType::As);
3421                            if has_as
3422                                || self.check(TokenType::Identifier)
3423                                || self.check(TokenType::Var)
3424                            {
3425                                let alias_name = self.advance().text;
3426                                with_offset_alias = Some(crate::expressions::Identifier {
3427                                    name: alias_name,
3428                                    quoted: false,
3429                                    trailing_comments: Vec::new(),
3430                                    span: None,
3431                                });
3432                            }
3433                        }
3434                        Some(Box::new(Expression::Boolean(BooleanLiteral {
3435                            value: true,
3436                        })))
3437                    } else {
3438                        // Not ORDINALITY or OFFSET, put back WITH
3439                        self.current -= 1;
3440                        None
3441                    }
3442                } else {
3443                    None
3444                };
3445
3446                // Update the inner UnnestFunc with WITH ORDINALITY/OFFSET info
3447                if ordinality.is_some() {
3448                    if let Expression::Unnest(ref mut u) = func_expr {
3449                        u.with_ordinality = true;
3450                        u.offset_alias = with_offset_alias;
3451                    }
3452                }
3453
3454                // Parse optional alias: AS alias or just alias
3455                let alias_ident = if self.match_token(TokenType::As) {
3456                    Some(self.expect_identifier_or_keyword_with_quoted()?)
3457                } else if !self.is_at_end()
3458                    && !self.check(TokenType::Comma)
3459                    && !self.check(TokenType::RParen)
3460                    && !self.check(TokenType::On)
3461                    && !self.check(TokenType::Cross)
3462                    && !self.check(TokenType::Inner)
3463                    && !self.check(TokenType::Left)
3464                    && !self.check(TokenType::Right)
3465                    && !self.check(TokenType::Full)
3466                    && !self.check(TokenType::Join)
3467                    && !self.check(TokenType::Where)
3468                    && !self.check(TokenType::Order)
3469                    && !self.check(TokenType::Limit)
3470                    && !self.check(TokenType::Semicolon)
3471                    && (self.check(TokenType::Identifier) || self.check(TokenType::Var))
3472                {
3473                    Some(self.expect_identifier_or_keyword_with_quoted()?)
3474                } else {
3475                    None
3476                };
3477                let alias_quoted = alias_ident.as_ref().map_or(false, |id| id.quoted);
3478                let alias = alias_ident.map(|id| id.name);
3479
3480                // Parse column aliases: (col1, col2, ...)
3481                let column_aliases = if alias.is_some() && self.match_token(TokenType::LParen) {
3482                    let mut cols = Vec::new();
3483                    loop {
3484                        cols.push(self.expect_identifier_or_keyword()?);
3485                        if !self.match_token(TokenType::Comma) {
3486                            break;
3487                        }
3488                    }
3489                    self.expect(TokenType::RParen)?;
3490                    cols
3491                } else {
3492                    Vec::new()
3493                };
3494
3495                Expression::Lateral(Box::new(Lateral {
3496                    this: Box::new(func_expr),
3497                    view: None,
3498                    outer: None,
3499                    alias,
3500                    alias_quoted,
3501                    cross_apply: None,
3502                    ordinality,
3503                    column_aliases,
3504                }))
3505            }
3506        } else if self.match_token(TokenType::LParen) {
3507            // Subquery or parenthesized set operation or (VALUES ...)
3508            if self.check(TokenType::Values) {
3509                // (VALUES (...), (...)) AS t(c1, c2) or (VALUES (0) foo(bar))
3510                let mut values = self.parse_values()?;
3511                self.expect(TokenType::RParen)?;
3512                // Extract alias from Values if present and move to Subquery
3513                let (alias, column_aliases) = if let Expression::Values(ref mut v) = values {
3514                    (v.alias.take(), std::mem::take(&mut v.column_aliases))
3515                } else {
3516                    (None, Vec::new())
3517                };
3518                Expression::Subquery(Box::new(Subquery {
3519                    this: values,
3520                    alias,
3521                    column_aliases,
3522                    order_by: None,
3523                    limit: None,
3524                    offset: None,
3525                    distribute_by: None,
3526                    sort_by: None,
3527                    cluster_by: None,
3528                    lateral: false,
3529                    modifiers_inside: false,
3530                    trailing_comments: self.previous_trailing_comments().to_vec(),
3531                    inferred_type: None,
3532                }))
3533            } else if self.check(TokenType::Select)
3534                || self.check(TokenType::With)
3535                || self.check(TokenType::Pivot)
3536                || self.check(TokenType::Unpivot)
3537                || self.check(TokenType::From)
3538                || self.check(TokenType::Merge)
3539                || self.check(TokenType::Describe)
3540                || (self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EXPLAIN"))
3541                || (self.check(TokenType::Var)
3542                    && self.peek().text.eq_ignore_ascii_case("SUMMARIZE"))
3543            {
3544                let query = self.parse_statement()?;
3545                self.expect(TokenType::RParen)?;
3546                let trailing = self.previous_trailing_comments().to_vec();
3547                // Check for set operations after parenthesized query
3548                // If there's a set operation, wrap query in Subquery first to preserve parens
3549                // e.g., (SELECT 1) UNION (SELECT 2) - the left operand needs Subquery wrapper
3550                let result = if self.check(TokenType::Union)
3551                    || self.check(TokenType::Intersect)
3552                    || self.check(TokenType::Except)
3553                {
3554                    let left = Expression::Subquery(Box::new(Subquery {
3555                        this: query,
3556                        alias: None,
3557                        column_aliases: Vec::new(),
3558                        order_by: None,
3559                        limit: None,
3560                        offset: None,
3561                        lateral: false,
3562                        modifiers_inside: false,
3563                        trailing_comments: Vec::new(),
3564                        distribute_by: None,
3565                        sort_by: None,
3566                        cluster_by: None,
3567                        inferred_type: None,
3568                    }));
3569                    self.parse_set_operation(left)?
3570                } else {
3571                    query
3572                };
3573                Expression::Subquery(Box::new(Subquery {
3574                    this: result,
3575                    alias: None,
3576                    column_aliases: Vec::new(),
3577                    order_by: None,
3578                    limit: None,
3579                    offset: None,
3580                    distribute_by: None,
3581                    sort_by: None,
3582                    cluster_by: None,
3583                    lateral: false,
3584                    modifiers_inside: false,
3585                    trailing_comments: trailing,
3586                    inferred_type: None,
3587                }))
3588            } else if self.check(TokenType::LParen) {
3589                // Nested parens like ((SELECT ...)) or ((x))
3590                // Also handles ((SELECT 1) UNION (SELECT 2)) - set operations inside parens
3591                let inner = self.parse_table_expression()?;
3592
3593                // Handle alias on subquery before set operation: ((SELECT 1) AS a UNION ALL (SELECT 2) AS b)
3594                let inner = if self.match_token(TokenType::As) {
3595                    let alias = self.expect_identifier()?;
3596                    if let Expression::Subquery(mut subq) = inner {
3597                        subq.alias = Some(Identifier::new(alias));
3598                        Expression::Subquery(subq)
3599                    } else {
3600                        Expression::Alias(Box::new(Alias::new(inner, Identifier::new(alias))))
3601                    }
3602                } else if self.is_identifier_token()
3603                    && !self.check(TokenType::Union)
3604                    && !self.check(TokenType::Intersect)
3605                    && !self.check(TokenType::Except)
3606                    && !self.check(TokenType::Cross)
3607                    && !self.check(TokenType::Inner)
3608                    && !self.check(TokenType::Left)
3609                    && !self.check(TokenType::Right)
3610                    && !self.check(TokenType::Full)
3611                    && !self.check(TokenType::Join)
3612                    && !self.check(TokenType::Order)
3613                    && !self.check(TokenType::Limit)
3614                    && !self.check(TokenType::Offset)
3615                    && !self.check(TokenType::Xor)
3616                {
3617                    // Implicit alias (no AS keyword)
3618                    let alias = self.expect_identifier()?;
3619                    if let Expression::Subquery(mut subq) = inner {
3620                        subq.alias = Some(Identifier::new(alias));
3621                        Expression::Subquery(subq)
3622                    } else {
3623                        Expression::Alias(Box::new(Alias::new(inner, Identifier::new(alias))))
3624                    }
3625                } else {
3626                    inner
3627                };
3628
3629                // ClickHouse: ((SELECT 1) AS x, (SELECT 2) AS y) — tuple of aliased subqueries
3630                if matches!(
3631                    self.config.dialect,
3632                    Some(crate::dialects::DialectType::ClickHouse)
3633                ) && self.check(TokenType::Comma)
3634                {
3635                    let mut exprs = vec![inner];
3636                    while self.match_token(TokenType::Comma) {
3637                        if self.check(TokenType::RParen) {
3638                            break;
3639                        }
3640                        let e = self.parse_expression()?;
3641                        exprs.push(e);
3642                    }
3643                    self.expect(TokenType::RParen)?;
3644                    return Ok(Expression::Tuple(Box::new(Tuple { expressions: exprs })));
3645                }
3646
3647                // Check for set operations after the first table expression
3648                let had_set_operation = self.check(TokenType::Union)
3649                    || self.check(TokenType::Intersect)
3650                    || self.check(TokenType::Except);
3651                let result = if had_set_operation {
3652                    // This is a set operation like ((SELECT 1) UNION (SELECT 2))
3653                    // Wrap inner in a subquery-like expression and parse set operation
3654                    let set_result = self.parse_set_operation(inner)?;
3655                    set_result
3656                } else if self.check(TokenType::Cross)
3657                    || self.check(TokenType::Inner)
3658                    || self.check(TokenType::Left)
3659                    || self.check(TokenType::Right)
3660                    || self.check(TokenType::Full)
3661                    || self.check(TokenType::Join)
3662                {
3663                    // This is a join: ((SELECT 1) CROSS JOIN (SELECT 2))
3664                    let joins = self.parse_joins()?;
3665                    let lateral_views = self.parse_lateral_views()?;
3666                    Expression::JoinedTable(Box::new(JoinedTable {
3667                        left: inner,
3668                        joins,
3669                        lateral_views,
3670                        alias: None,
3671                    }))
3672                } else {
3673                    inner
3674                };
3675
3676                // Handle ORDER BY, LIMIT, OFFSET after set operations inside parens
3677                let result = if self.check(TokenType::Order) {
3678                    // Wrap in a subquery with order/limit
3679                    self.expect(TokenType::Order)?;
3680                    self.expect(TokenType::By)?;
3681                    let order_by = self.parse_order_by()?;
3682                    let limit = if self.match_token(TokenType::Limit) {
3683                        Some(Limit {
3684                            this: self.parse_expression()?,
3685                            percent: false,
3686                            comments: Vec::new(),
3687                        })
3688                    } else {
3689                        None
3690                    };
3691                    let offset = if self.match_token(TokenType::Offset) {
3692                        Some(Offset {
3693                            this: self.parse_expression()?,
3694                            rows: None,
3695                        })
3696                    } else {
3697                        None
3698                    };
3699                    Expression::Subquery(Box::new(Subquery {
3700                        this: result,
3701                        alias: None,
3702                        column_aliases: Vec::new(),
3703                        order_by: Some(order_by),
3704                        limit,
3705                        offset,
3706                        distribute_by: None,
3707                        sort_by: None,
3708                        cluster_by: None,
3709                        lateral: false,
3710                        modifiers_inside: true, // ORDER BY was inside the parens
3711                        trailing_comments: Vec::new(),
3712                        inferred_type: None,
3713                    }))
3714                } else if self.check(TokenType::Limit) || self.check(TokenType::Offset) {
3715                    // LIMIT/OFFSET without ORDER BY
3716                    let limit = if self.match_token(TokenType::Limit) {
3717                        Some(Limit {
3718                            this: self.parse_expression()?,
3719                            percent: false,
3720                            comments: Vec::new(),
3721                        })
3722                    } else {
3723                        None
3724                    };
3725                    let offset = if self.match_token(TokenType::Offset) {
3726                        Some(Offset {
3727                            this: self.parse_expression()?,
3728                            rows: None,
3729                        })
3730                    } else {
3731                        None
3732                    };
3733                    Expression::Subquery(Box::new(Subquery {
3734                        this: result,
3735                        alias: None,
3736                        column_aliases: Vec::new(),
3737                        order_by: None,
3738                        limit,
3739                        offset,
3740                        distribute_by: None,
3741                        sort_by: None,
3742                        cluster_by: None,
3743                        lateral: false,
3744                        modifiers_inside: true, // LIMIT/OFFSET was inside the parens
3745                        trailing_comments: Vec::new(),
3746                        inferred_type: None,
3747                    }))
3748                } else {
3749                    result
3750                };
3751
3752                self.expect(TokenType::RParen)?;
3753                // Wrap result in Paren to preserve the outer parentheses when needed
3754                // Cases:
3755                // - ((SELECT 1)) -> Paren(Subquery(Select)) - inner was subquery of SELECT, wrap in Paren
3756                // - ((SELECT 1) UNION (SELECT 2)) -> Subquery(Union) - recursive call handled set op, don't add Paren
3757                // - ((SELECT 1) AS a UNION ALL ...) -> Union - we handled set op, need to add Paren
3758                // - (((SELECT 1) UNION SELECT 2) ORDER BY x) -> Subquery with modifiers_inside=true
3759                let had_modifiers = matches!(&result, Expression::Subquery(s) if s.order_by.is_some() || s.limit.is_some() || s.offset.is_some());
3760                let result_is_subquery_of_set_op = matches!(&result, Expression::Subquery(s) if matches!(&s.this, Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)));
3761                if had_modifiers || result_is_subquery_of_set_op {
3762                    // Subquery with modifiers or Subquery(Union) - already has proper structure
3763                    result
3764                } else {
3765                    // All other cases need Paren wrapper to preserve outer parentheses
3766                    Expression::Paren(Box::new(Paren {
3767                        this: result,
3768                        trailing_comments: Vec::new(),
3769                    }))
3770                }
3771            } else if self.is_identifier_token()
3772                || self.is_safe_keyword_as_identifier()
3773                || self.can_be_alias_keyword()
3774            {
3775                // Parenthesized join expression: (tbl1 CROSS JOIN tbl2) or just (x)
3776                // Also allow safe keywords and alias keywords (all, left, etc.) as table names
3777                let (left, joins) = self.parse_table_expression_with_joins()?;
3778                // Parse LATERAL VIEW after joins: (x CROSS JOIN foo LATERAL VIEW EXPLODE(y))
3779                let lateral_views = self.parse_lateral_views()?;
3780                self.expect(TokenType::RParen)?;
3781                if joins.is_empty() && lateral_views.is_empty() {
3782                    // Just a parenthesized table expression, wrap in Paren to preserve parens
3783                    Expression::Paren(Box::new(Paren {
3784                        this: left,
3785                        trailing_comments: Vec::new(),
3786                    }))
3787                } else {
3788                    // Create a JoinedTable
3789                    Expression::JoinedTable(Box::new(JoinedTable {
3790                        left,
3791                        joins,
3792                        lateral_views,
3793                        alias: None, // Alias is parsed separately after this
3794                    }))
3795                }
3796            } else {
3797                let query = self.parse_statement()?;
3798                self.expect(TokenType::RParen)?;
3799                Expression::Subquery(Box::new(Subquery {
3800                    this: query,
3801                    alias: None,
3802                    column_aliases: Vec::new(),
3803                    order_by: None,
3804                    limit: None,
3805                    offset: None,
3806                    distribute_by: None,
3807                    sort_by: None,
3808                    cluster_by: None,
3809                    lateral: false,
3810                    modifiers_inside: false,
3811                    trailing_comments: self.previous_trailing_comments().to_vec(),
3812                    inferred_type: None,
3813                }))
3814            }
3815        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() || self.can_be_alias_keyword()
3816            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) && self.check(TokenType::Number))
3817            || self.is_mysql_numeric_identifier()
3818            // PIVOT/UNPIVOT can be table names when not followed by (
3819            || (self.check(TokenType::Pivot) && !self.check_next(TokenType::LParen))
3820            || (self.check(TokenType::Unpivot) && !self.check_next(TokenType::LParen))
3821            // ClickHouse: braced query parameters as table names {db:Identifier}.table
3822            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) && self.check(TokenType::LBrace))
3823            // ClickHouse: allow union/except/intersect as table names when not followed by ALL/DISTINCT/SELECT/(
3824            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
3825                && (self.check(TokenType::Union) || self.check(TokenType::Except) || self.check(TokenType::Intersect))
3826                && !self.check_next(TokenType::All) && !self.check_next(TokenType::Distinct)
3827                && !self.check_next(TokenType::Select) && !self.check_next(TokenType::LParen))
3828        {
3829            // Table name - could be simple, qualified, or table function
3830            // Also allow safe keywords (like 'table', 'view', 'case', 'all', etc.) as table names
3831            // BigQuery: also allows numeric table parts and hyphenated identifiers
3832            // MySQL: allows numeric-starting identifiers (e.g., 00f, 1d)
3833
3834            // DuckDB prefix alias syntax: alias: table (e.g., "foo: bar" means "bar AS foo")
3835            // Check if next token is COLON (but not :: which is DCOLON for casts)
3836            if matches!(
3837                self.config.dialect,
3838                Some(crate::dialects::DialectType::DuckDB)
3839            ) && self.check_next(TokenType::Colon)
3840                && !(self.current + 2 < self.tokens.len()
3841                    && self.tokens[self.current + 2].token_type == TokenType::Colon)
3842            {
3843                // Parse the alias identifier
3844                let alias_ident = self.parse_bigquery_table_part()?;
3845                let pre_alias_comments = self.previous_trailing_comments().to_vec();
3846                // Consume the colon
3847                self.expect(TokenType::Colon)?;
3848                let colon_comments = self.previous_trailing_comments().to_vec();
3849                // Parse the actual table expression recursively
3850                let mut table_expr = self.parse_table_expression()?;
3851                // Merge comments
3852                let mut all_comments = pre_alias_comments;
3853                all_comments.extend(colon_comments);
3854                // Apply the alias to the table expression
3855                match &mut table_expr {
3856                    Expression::Table(ref mut t) => {
3857                        t.alias = Some(alias_ident);
3858                        t.alias_explicit_as = true; // Output AS keyword (required by expected format)
3859                                                    // Store prefix alias comments - they should come BEFORE the table's trailing comments
3860                                                    // For "foo /* bla */: bar /* baz */", output is "bar AS foo /* bla */ /* baz */"
3861                                                    // So alias comments (/* bla */) come first, then table comments (/* baz */)
3862                        if !all_comments.is_empty() {
3863                            let existing_comments = std::mem::take(&mut t.trailing_comments);
3864                            t.trailing_comments = all_comments;
3865                            t.trailing_comments.extend(existing_comments);
3866                        }
3867                    }
3868                    Expression::Subquery(ref mut s) => {
3869                        s.alias = Some(alias_ident);
3870                    }
3871                    Expression::Function(ref mut _f) => {
3872                        // Wrap function in alias
3873                        return Ok(Expression::Alias(Box::new(Alias {
3874                            this: table_expr,
3875                            alias: alias_ident,
3876                            column_aliases: Vec::new(),
3877                            pre_alias_comments: all_comments,
3878                            trailing_comments: Vec::new(),
3879                            inferred_type: None,
3880                        })));
3881                    }
3882                    _ => {
3883                        // For other expressions, wrap in Alias
3884                        return Ok(Expression::Alias(Box::new(Alias {
3885                            this: table_expr,
3886                            alias: alias_ident,
3887                            column_aliases: Vec::new(),
3888                            pre_alias_comments: all_comments,
3889                            trailing_comments: Vec::new(),
3890                            inferred_type: None,
3891                        })));
3892                    }
3893                }
3894                return Ok(table_expr);
3895            }
3896
3897            let first_ident = self.parse_bigquery_table_part()?;
3898            let first_name = first_ident.name.clone();
3899
3900            // Check for qualified name (schema.table) or table function
3901            if self.match_token(TokenType::Dot) {
3902                // Handle TSQL a..b syntax (database..table with empty schema)
3903                if self.check(TokenType::Dot) {
3904                    // Two consecutive dots: a..b means catalog..table (empty schema)
3905                    self.skip(); // consume second dot
3906                    let table_ident = self.parse_bigquery_table_part()?;
3907                    let trailing_comments = self.previous_trailing_comments().to_vec();
3908                    return Ok(Expression::boxed_table(TableRef {
3909                        catalog: Some(first_ident),
3910                        schema: Some(Identifier::new("")), // Empty schema represents ..
3911                        name: table_ident,
3912                        alias: None,
3913                        alias_explicit_as: false,
3914                        column_aliases: Vec::new(),
3915                        leading_comments: Vec::new(),
3916                        trailing_comments,
3917                        when: None,
3918                        only: false,
3919                        final_: false,
3920                        table_sample: None,
3921                        hints: Vec::new(),
3922                        system_time: None,
3923                        partitions: Vec::new(),
3924                        identifier_func: None,
3925                        changes: None,
3926                        version: None,
3927                        span: None,
3928                    }));
3929                }
3930
3931                // BigQuery: handle x.* wildcard table reference (e.g., SELECT * FROM x.*)
3932                // After the first dot, if we see a Star token, it's a wildcard table name
3933                if matches!(
3934                    self.config.dialect,
3935                    Some(crate::dialects::DialectType::BigQuery)
3936                ) && self.check(TokenType::Star)
3937                {
3938                    self.skip(); // consume *
3939                    let trailing_comments = self.previous_trailing_comments().to_vec();
3940                    return Ok(Expression::boxed_table(TableRef {
3941                        catalog: None,
3942                        schema: Some(first_ident),
3943                        name: Identifier::new("*"),
3944                        alias: None,
3945                        alias_explicit_as: false,
3946                        column_aliases: Vec::new(),
3947                        leading_comments: Vec::new(),
3948                        trailing_comments,
3949                        when: None,
3950                        only: false,
3951                        final_: false,
3952                        table_sample: None,
3953                        hints: Vec::new(),
3954                        system_time: None,
3955                        partitions: Vec::new(),
3956                        identifier_func: None,
3957                        changes: None,
3958                        version: None,
3959                        span: None,
3960                    }));
3961                }
3962
3963                // schema.table or schema.function()
3964                // Allow keywords as table/schema names (e.g., schema.table, catalog.view)
3965                let second_ident = self.parse_bigquery_table_part()?;
3966                let second_name = second_ident.name.clone();
3967
3968                if self.match_token(TokenType::Dot) {
3969                    // BigQuery: handle a.b.* wildcard table reference
3970                    if matches!(
3971                        self.config.dialect,
3972                        Some(crate::dialects::DialectType::BigQuery)
3973                    ) && self.check(TokenType::Star)
3974                    {
3975                        self.skip(); // consume *
3976                        let trailing_comments = self.previous_trailing_comments().to_vec();
3977                        return Ok(Expression::boxed_table(TableRef {
3978                            catalog: Some(first_ident),
3979                            schema: Some(second_ident),
3980                            name: Identifier::new("*"),
3981                            alias: None,
3982                            alias_explicit_as: false,
3983                            column_aliases: Vec::new(),
3984                            leading_comments: Vec::new(),
3985                            trailing_comments,
3986                            when: None,
3987                            only: false,
3988                            final_: false,
3989                            table_sample: None,
3990                            hints: Vec::new(),
3991                            system_time: None,
3992                            partitions: Vec::new(),
3993                            identifier_func: None,
3994                            changes: None,
3995                            version: None,
3996                            span: None,
3997                        }));
3998                    }
3999                    // catalog.schema.table or catalog.schema.function()
4000                    let third_ident = self.parse_bigquery_table_part()?;
4001                    let third_name = third_ident.name.clone();
4002
4003                    // Check for 4-part name (e.g., project.dataset.INFORMATION_SCHEMA.TABLES)
4004                    if self.match_token(TokenType::Dot) {
4005                        let fourth_ident = self.parse_bigquery_table_part()?;
4006                        // BigQuery wildcard table suffix: a.b.c.d* matches all tables starting with d
4007                        let mut table_name = fourth_ident;
4008                        if matches!(
4009                            self.config.dialect,
4010                            Some(crate::dialects::DialectType::BigQuery)
4011                        ) && self.check(TokenType::Star)
4012                            && self.is_connected()
4013                        {
4014                            self.skip(); // consume *
4015                            table_name.name.push('*');
4016                        }
4017                        let trailing_comments = self.previous_trailing_comments().to_vec();
4018                        // For 4-part names, combine first two parts as catalog, third as schema
4019                        Expression::boxed_table(TableRef {
4020                            catalog: Some(Identifier::new(format!(
4021                                "{}.{}",
4022                                first_name, second_name
4023                            ))),
4024                            schema: Some(third_ident),
4025                            name: table_name,
4026                            alias: None,
4027                            alias_explicit_as: false,
4028                            column_aliases: Vec::new(),
4029                            leading_comments: Vec::new(),
4030                            trailing_comments,
4031                            when: None,
4032                            only: false,
4033                            final_: false,
4034                            table_sample: None,
4035                            hints: Vec::new(),
4036                            system_time: None,
4037                            partitions: Vec::new(),
4038                            identifier_func: None,
4039                            changes: None,
4040                            version: None,
4041                            span: None,
4042                        })
4043                    } else if self.match_token(TokenType::LParen) {
4044                        // catalog.schema.function() - table-valued function
4045                        let args = if self.check(TokenType::RParen) {
4046                            Vec::new()
4047                        } else {
4048                            self.parse_function_arguments()?
4049                        };
4050                        self.expect(TokenType::RParen)?;
4051                        let trailing_comments = self.previous_trailing_comments().to_vec();
4052                        Expression::Function(Box::new(Function {
4053                            name: format!("{}.{}.{}", first_name, second_name, third_name),
4054                            args,
4055                            distinct: false,
4056                            trailing_comments,
4057                            use_bracket_syntax: false,
4058                            no_parens: false,
4059                            quoted: false,
4060                            span: None,
4061                            inferred_type: None,
4062                        }))
4063                    } else {
4064                        // catalog.schema.table
4065                        // BigQuery wildcard table suffix: x.y.z* matches all tables starting with z
4066                        let mut table_name = third_ident;
4067                        if matches!(
4068                            self.config.dialect,
4069                            Some(crate::dialects::DialectType::BigQuery)
4070                        ) && self.check(TokenType::Star)
4071                            && self.is_connected()
4072                        {
4073                            self.skip(); // consume *
4074                            table_name.name.push('*');
4075                        }
4076                        let trailing_comments = self.previous_trailing_comments().to_vec();
4077                        Expression::boxed_table(TableRef {
4078                            catalog: Some(first_ident),
4079                            schema: Some(second_ident),
4080                            name: table_name,
4081                            alias: None,
4082                            alias_explicit_as: false,
4083                            column_aliases: Vec::new(),
4084                            leading_comments: Vec::new(),
4085                            trailing_comments,
4086                            when: None,
4087                            only: false,
4088                            final_: false,
4089                            table_sample: None,
4090                            hints: Vec::new(),
4091                            system_time: None,
4092                            partitions: Vec::new(),
4093                            identifier_func: None,
4094                            changes: None,
4095                            version: None,
4096                            span: None,
4097                        })
4098                    }
4099                } else if self.match_token(TokenType::LParen) {
4100                    // schema.function() - table-valued function
4101                    let args = if self.check(TokenType::RParen) {
4102                        Vec::new()
4103                    } else {
4104                        self.parse_function_arguments()?
4105                    };
4106                    self.expect(TokenType::RParen)?;
4107                    let trailing_comments = self.previous_trailing_comments().to_vec();
4108                    Expression::Function(Box::new(Function {
4109                        name: format!("{}.{}", first_name, second_name),
4110                        args,
4111                        distinct: false,
4112                        trailing_comments,
4113                        use_bracket_syntax: false,
4114                        no_parens: false,
4115                        quoted: false,
4116                        span: None,
4117                        inferred_type: None,
4118                    }))
4119                } else {
4120                    // schema.table
4121                    // BigQuery wildcard table suffix: x.y* matches all tables starting with y
4122                    let mut table_name = second_ident;
4123                    if matches!(
4124                        self.config.dialect,
4125                        Some(crate::dialects::DialectType::BigQuery)
4126                    ) && self.check(TokenType::Star)
4127                        && self.is_connected()
4128                    {
4129                        self.skip(); // consume *
4130                        table_name.name.push('*');
4131                    }
4132                    let trailing_comments = self.previous_trailing_comments().to_vec();
4133                    Expression::boxed_table(TableRef {
4134                        catalog: None,
4135                        schema: Some(first_ident),
4136                        name: table_name,
4137                        alias: None,
4138                        alias_explicit_as: false,
4139                        column_aliases: Vec::new(),
4140                        leading_comments: Vec::new(),
4141                        trailing_comments,
4142                        when: None,
4143                        only: false,
4144                        final_: false,
4145                        table_sample: None,
4146                        hints: Vec::new(),
4147                        system_time: None,
4148                        partitions: Vec::new(),
4149                        identifier_func: None,
4150                        changes: None,
4151                        version: None,
4152                        span: None,
4153                    })
4154                }
4155            } else if self.match_token(TokenType::LParen) {
4156                // Handle JSON_TABLE specially - it has COLUMNS clause syntax
4157                if first_name.eq_ignore_ascii_case("JSON_TABLE") {
4158                    // Parse the JSON expression (use parse_bitwise to avoid consuming FORMAT)
4159                    let this = self
4160                        .parse_bitwise()?
4161                        .unwrap_or(Expression::Null(crate::expressions::Null));
4162
4163                    // Check for FORMAT JSON after the expression
4164                    let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
4165                        Expression::JSONFormat(Box::new(crate::expressions::JSONFormat {
4166                            this: Some(Box::new(this)),
4167                            options: Vec::new(),
4168                            is_json: None,
4169                            to_json: None,
4170                        }))
4171                    } else {
4172                        this
4173                    };
4174
4175                    // Parse path (after comma)
4176                    let path = if self.match_token(TokenType::Comma) {
4177                        if let Some(s) = self.parse_string()? {
4178                            Some(Box::new(s))
4179                        } else {
4180                            None
4181                        }
4182                    } else {
4183                        None
4184                    };
4185
4186                    // Oracle uses "ERROR ON ERROR" (value then behavior) instead of "ON ERROR ERROR"
4187                    // Parse error handling: ERROR ON ERROR or NULL ON ERROR
4188                    let error_handling = if self.match_identifier("ERROR")
4189                        && self.match_text_seq(&["ON", "ERROR"])
4190                    {
4191                        Some(Box::new(Expression::Var(Box::new(Var {
4192                            this: "ERROR ON ERROR".to_string(),
4193                        }))))
4194                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
4195                        Some(Box::new(Expression::Var(Box::new(Var {
4196                            this: "NULL ON ERROR".to_string(),
4197                        }))))
4198                    } else {
4199                        None
4200                    };
4201
4202                    // Parse empty handling: ERROR ON EMPTY or NULL ON EMPTY
4203                    let empty_handling = if self.match_identifier("ERROR")
4204                        && self.match_text_seq(&["ON", "EMPTY"])
4205                    {
4206                        Some(Box::new(Expression::Var(Box::new(Var {
4207                            this: "ERROR ON EMPTY".to_string(),
4208                        }))))
4209                    } else if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
4210                        Some(Box::new(Expression::Var(Box::new(Var {
4211                            this: "NULL ON EMPTY".to_string(),
4212                        }))))
4213                    } else {
4214                        None
4215                    };
4216
4217                    // Parse COLUMNS clause
4218                    let schema = self.parse_json_table_columns()?;
4219
4220                    self.expect(TokenType::RParen)?;
4221
4222                    Expression::JSONTable(Box::new(JSONTable {
4223                        this: Box::new(this_with_format),
4224                        schema: schema.map(Box::new),
4225                        path,
4226                        error_handling,
4227                        empty_handling,
4228                    }))
4229                } else if first_name.eq_ignore_ascii_case("XMLTABLE") {
4230                    // Handle XMLTABLE specially - it has COLUMNS clause syntax
4231                    // XMLTABLE([XMLNAMESPACES(...),] '/xpath' PASSING xml_doc COLUMNS ...)
4232                    if let Some(xml_table) = self.parse_xml_table()? {
4233                        self.expect(TokenType::RParen)?;
4234                        xml_table
4235                    } else {
4236                        return Err(self.parse_error("Failed to parse XMLTABLE"));
4237                    }
4238                } else if first_name.eq_ignore_ascii_case("OPENJSON") {
4239                    // Handle OPENJSON specially - it has WITH clause for column definitions
4240                    // OPENJSON(json[, path]) [WITH (col1 type1 'path' [AS JSON], ...)]
4241                    if let Some(openjson_expr) = self.parse_open_json()? {
4242                        openjson_expr
4243                    } else {
4244                        return Err(self.parse_error("Failed to parse OPENJSON"));
4245                    }
4246                } else if first_name.eq_ignore_ascii_case("SEMANTIC_VIEW") {
4247                    // Handle SEMANTIC_VIEW specially - it has METRICS/DIMENSIONS/FACTS/WHERE syntax
4248                    // SEMANTIC_VIEW(table METRICS a.b, a.c DIMENSIONS a.b, a.c WHERE expr)
4249                    let semantic_view = self.parse_semantic_view()?;
4250                    self.expect(TokenType::RParen)?;
4251                    semantic_view
4252                } else if (first_name.eq_ignore_ascii_case("view")
4253                    || first_name.eq_ignore_ascii_case("merge"))
4254                    && (self.check(TokenType::Select) || self.check(TokenType::With))
4255                {
4256                    // ClickHouse: view(SELECT ...) and merge(SELECT ...) table functions
4257                    // contain a subquery as the argument
4258                    let query = self.parse_statement()?;
4259                    self.expect(TokenType::RParen)?;
4260                    let trailing_comments = self.previous_trailing_comments().to_vec();
4261                    Expression::Function(Box::new(Function {
4262                        name: first_name.to_string(),
4263                        args: vec![query],
4264                        distinct: false,
4265                        trailing_comments,
4266                        use_bracket_syntax: false,
4267                        no_parens: false,
4268                        quoted: false,
4269                        span: None,
4270                        inferred_type: None,
4271                    }))
4272                } else {
4273                    // Simple table function like UNNEST(), GAP_FILL(), etc.
4274                    let args = if self.check(TokenType::RParen) {
4275                        Vec::new()
4276                    } else {
4277                        self.parse_function_arguments()?
4278                    };
4279                    self.expect(TokenType::RParen)?;
4280                    let trailing_comments = self.previous_trailing_comments().to_vec();
4281
4282                    // Handle UNNEST specially to create UnnestFunc expression
4283                    if first_name.eq_ignore_ascii_case("UNNEST") {
4284                        // Check for WITH ORDINALITY (Presto) or WITH OFFSET (BigQuery)
4285                        // Both are semantically the same - provide an ordinal/offset column
4286                        let with_ordinality = self
4287                            .match_keywords(&[TokenType::With, TokenType::Ordinality])
4288                            || self.match_text_seq(&["WITH", "OFFSET"]);
4289                        // If WITH OFFSET matched, check for optional offset alias: WITH OFFSET AS y or WITH OFFSET y
4290                        let offset_alias = if with_ordinality
4291                            && matches!(
4292                                self.config.dialect,
4293                                Some(crate::dialects::DialectType::BigQuery)
4294                            ) {
4295                            let has_as = self.match_token(TokenType::As);
4296                            if has_as
4297                                || (self.check(TokenType::Identifier) || self.check(TokenType::Var))
4298                            {
4299                                let alias_name = self.advance().text;
4300                                Some(crate::expressions::Identifier {
4301                                    name: alias_name,
4302                                    quoted: false,
4303                                    trailing_comments: Vec::new(),
4304                                    span: None,
4305                                })
4306                            } else {
4307                                None
4308                            }
4309                        } else {
4310                            None
4311                        };
4312                        let mut args_iter = args.into_iter();
4313                        let this = args_iter
4314                            .next()
4315                            .ok_or_else(|| self.parse_error("Expected expression in UNNEST"))?;
4316                        let expressions: Vec<Expression> = args_iter.collect();
4317                        Expression::Unnest(Box::new(crate::expressions::UnnestFunc {
4318                            this,
4319                            expressions,
4320                            with_ordinality,
4321                            alias: None,
4322                            offset_alias,
4323                        }))
4324                    } else {
4325                        // Check for WITH ORDINALITY after any table-valued function
4326                        let with_ordinality =
4327                            self.match_keywords(&[TokenType::With, TokenType::Ordinality]);
4328                        let func_name = if with_ordinality {
4329                            format!("{} WITH ORDINALITY", first_name)
4330                        } else {
4331                            first_name.clone()
4332                        };
4333                        let func = Function {
4334                            name: func_name,
4335                            args,
4336                            distinct: false,
4337                            trailing_comments,
4338                            use_bracket_syntax: false,
4339                            no_parens: false,
4340                            quoted: false,
4341                            span: None,
4342                            inferred_type: None,
4343                        };
4344                        let func_expr = Expression::Function(Box::new(func));
4345
4346                        // TSQL: OPENDATASOURCE(...).Catalog.schema.table
4347                        // After a table-valued function, dot-chained access produces
4348                        // a TableRef whose identifier_func holds the function call.
4349                        if self.check(TokenType::Dot) {
4350                            self.skip(); // consume first dot
4351                            let part1 = self.parse_bigquery_table_part()?;
4352                            if self.match_token(TokenType::Dot) {
4353                                let part2 = self.parse_bigquery_table_part()?;
4354                                if self.match_token(TokenType::Dot) {
4355                                    // func().a.b.c  → catalog=a, schema=b, name=c
4356                                    let part3 = self.parse_bigquery_table_part()?;
4357                                    let tc = self.previous_trailing_comments().to_vec();
4358                                    Expression::boxed_table(TableRef {
4359                                        catalog: Some(part1),
4360                                        schema: Some(part2),
4361                                        name: part3,
4362                                        alias: None,
4363                                        alias_explicit_as: false,
4364                                        column_aliases: Vec::new(),
4365                                        leading_comments: Vec::new(),
4366                                        trailing_comments: tc,
4367                                        when: None,
4368                                        only: false,
4369                                        final_: false,
4370                                        table_sample: None,
4371                                        hints: Vec::new(),
4372                                        system_time: None,
4373                                        partitions: Vec::new(),
4374                                        identifier_func: Some(Box::new(func_expr)),
4375                                        changes: None,
4376                                        version: None,
4377                                        span: None,
4378                                    })
4379                                } else {
4380                                    // func().a.b  → schema=a, name=b
4381                                    let tc = self.previous_trailing_comments().to_vec();
4382                                    Expression::boxed_table(TableRef {
4383                                        catalog: None,
4384                                        schema: Some(part1),
4385                                        name: part2,
4386                                        alias: None,
4387                                        alias_explicit_as: false,
4388                                        column_aliases: Vec::new(),
4389                                        leading_comments: Vec::new(),
4390                                        trailing_comments: tc,
4391                                        when: None,
4392                                        only: false,
4393                                        final_: false,
4394                                        table_sample: None,
4395                                        hints: Vec::new(),
4396                                        system_time: None,
4397                                        partitions: Vec::new(),
4398                                        identifier_func: Some(Box::new(func_expr)),
4399                                        changes: None,
4400                                        version: None,
4401                                        span: None,
4402                                    })
4403                                }
4404                            } else {
4405                                // func().a  → name=a
4406                                let tc = self.previous_trailing_comments().to_vec();
4407                                Expression::boxed_table(TableRef {
4408                                    catalog: None,
4409                                    schema: None,
4410                                    name: part1,
4411                                    alias: None,
4412                                    alias_explicit_as: false,
4413                                    column_aliases: Vec::new(),
4414                                    leading_comments: Vec::new(),
4415                                    trailing_comments: tc,
4416                                    when: None,
4417                                    only: false,
4418                                    final_: false,
4419                                    table_sample: None,
4420                                    hints: Vec::new(),
4421                                    system_time: None,
4422                                    partitions: Vec::new(),
4423                                    identifier_func: Some(Box::new(func_expr)),
4424                                    changes: None,
4425                                    version: None,
4426                                    span: None,
4427                                })
4428                            }
4429                        } else {
4430                            func_expr
4431                        }
4432                    }
4433                }
4434            } else {
4435                // Simple table name
4436                // BigQuery wildcard table suffix: x* matches all tables starting with x
4437                let mut table_name = first_ident;
4438                if matches!(
4439                    self.config.dialect,
4440                    Some(crate::dialects::DialectType::BigQuery)
4441                ) && self.check(TokenType::Star)
4442                    && self.is_connected()
4443                {
4444                    self.skip(); // consume *
4445                    table_name.name.push('*');
4446                }
4447                let trailing_comments = self.previous_trailing_comments().to_vec();
4448                Expression::boxed_table(TableRef {
4449                    catalog: None,
4450                    schema: None,
4451                    name: table_name,
4452                    alias: None,
4453                    alias_explicit_as: false,
4454                    column_aliases: Vec::new(),
4455                    leading_comments: Vec::new(),
4456                    trailing_comments,
4457                    when: None,
4458                    only: false,
4459                    final_: false,
4460                    table_sample: None,
4461                    hints: Vec::new(),
4462                    system_time: None,
4463                    partitions: Vec::new(),
4464                    identifier_func: None,
4465                    changes: None,
4466                    version: None,
4467                    span: None,
4468                })
4469            }
4470        } else if self.check(TokenType::LBrace) {
4471            // ClickHouse query parameter: {name: Type}
4472            if let Some(param) = self.parse_clickhouse_braced_parameter()? {
4473                param
4474            } else {
4475                // Spark/Databricks widget template variable: {name}
4476                self.skip(); // consume {
4477                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4478                    let name_token = self.advance();
4479                    self.expect(TokenType::RBrace)?;
4480                    Expression::Parameter(Box::new(Parameter {
4481                        name: Some(name_token.text.clone()),
4482                        index: None,
4483                        style: ParameterStyle::Brace,
4484                        quoted: false,
4485                        string_quoted: false,
4486                        expression: None,
4487                    }))
4488                } else {
4489                    return Err(self.parse_error("Expected identifier after {"));
4490                }
4491            }
4492        } else if self.check(TokenType::Dollar) && self.check_next(TokenType::LBrace) {
4493            // Template variable as table reference: ${variable_name} or ${kind:name}
4494            // This is used in Databricks/Hive for parameterized queries
4495            self.skip(); // consume $
4496            self.skip(); // consume {
4497            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4498                let name_token = self.advance();
4499                // Check for ${kind:name} syntax (e.g., ${hiveconf:some_var})
4500                let expression = if self.match_token(TokenType::Colon) {
4501                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
4502                        let expr_token = self.advance();
4503                        Some(expr_token.text.clone())
4504                    } else {
4505                        return Err(self.parse_error("Expected identifier after : in ${...}"));
4506                    }
4507                } else {
4508                    None
4509                };
4510                self.expect(TokenType::RBrace)?;
4511                Expression::Parameter(Box::new(Parameter {
4512                    name: Some(name_token.text.clone()),
4513                    index: None,
4514                    style: ParameterStyle::DollarBrace,
4515                    quoted: false,
4516                    string_quoted: false,
4517                    expression,
4518                }))
4519            } else {
4520                return Err(self.parse_error("Expected identifier after ${"));
4521            }
4522        } else if self.check(TokenType::String) {
4523            // DuckDB allows string literals as table names: SELECT * FROM 'x.y'
4524            // Convert to a quoted identifier
4525            let string_token = self.advance();
4526            let table_name = Identifier {
4527                name: string_token.text.clone(),
4528                quoted: true,
4529                trailing_comments: Vec::new(),
4530                span: None,
4531            };
4532            let trailing_comments = self.previous_trailing_comments().to_vec();
4533            Expression::boxed_table(TableRef {
4534                catalog: None,
4535                schema: None,
4536                name: table_name,
4537                alias: None,
4538                alias_explicit_as: false,
4539                column_aliases: Vec::new(),
4540                leading_comments: Vec::new(),
4541                trailing_comments,
4542                when: None,
4543                only: false,
4544                final_: false,
4545                table_sample: None,
4546                hints: Vec::new(),
4547                system_time: None,
4548                partitions: Vec::new(),
4549                identifier_func: None,
4550                changes: None,
4551                version: None,
4552                span: None,
4553            })
4554        } else {
4555            return Err(self.parse_error(format!(
4556                "Expected table name or subquery, got {:?}",
4557                self.peek().token_type
4558            )));
4559        };
4560
4561        // Postgres supports a wildcard (table) suffix operator, which is a no-op in this context.
4562        // e.g., FROM t1* means "include inherited tables". Matches Python sqlglot behavior.
4563        self.match_token(TokenType::Star);
4564
4565        // Check for Snowflake CHANGES clause: CHANGES (INFORMATION => ...) AT|BEFORE (...) END (...)
4566        // Must be checked before time travel since CHANGES includes its own AT/BEFORE clauses
4567        if self.check_keyword_text("CHANGES") {
4568            if let Some(changes_expr) = self.parse_changes()? {
4569                if let Expression::Table(ref mut table) = expr {
4570                    if let Expression::Changes(changes_box) = changes_expr {
4571                        table.changes = Some(changes_box);
4572                    }
4573                }
4574            }
4575        }
4576
4577        // Check for Snowflake time travel: BEFORE (STATEMENT => ...) or AT (TIMESTAMP => ...)
4578        if self.check(TokenType::Before) || self.check_keyword_text("AT") {
4579            if let Some(historical_expr) = self.parse_historical_data()? {
4580                // Attach historical data to the table expression
4581                if let Expression::Table(ref mut table) = expr {
4582                    if let Expression::HistoricalData(hd) = historical_expr {
4583                        table.when = Some(hd);
4584                    }
4585                }
4586            }
4587        }
4588
4589        // Check for TSQL FOR SYSTEM_TIME temporal clause (not BigQuery - handled post-alias)
4590        // Syntax: FOR SYSTEM_TIME AS OF expr
4591        //         FOR SYSTEM_TIME FROM expr TO expr
4592        //         FOR SYSTEM_TIME BETWEEN expr AND expr
4593        //         FOR SYSTEM_TIME CONTAINED IN (expr, expr)
4594        //         FOR SYSTEM_TIME ALL
4595        if !matches!(
4596            self.config.dialect,
4597            Some(crate::dialects::DialectType::BigQuery)
4598        ) && self.check(TokenType::For)
4599            && self.current + 1 < self.tokens.len()
4600            && self.tokens[self.current + 1]
4601                .text
4602                .eq_ignore_ascii_case("SYSTEM_TIME")
4603        {
4604            self.skip(); // consume FOR
4605            self.skip(); // consume SYSTEM_TIME
4606            let system_time_str = if self.match_token(TokenType::As) {
4607                // AS OF expr
4608                if self.check_keyword_text("OF") {
4609                    self.skip(); // consume OF
4610                    let start = self.current;
4611                    // Collect expression tokens until we hit a clause boundary
4612                    while !self.is_at_end()
4613                        && !self.check(TokenType::Semicolon)
4614                        && !self.check(TokenType::Where)
4615                        && !self.check(TokenType::Join)
4616                        && !self.check(TokenType::Left)
4617                        && !self.check(TokenType::Right)
4618                        && !self.check(TokenType::Inner)
4619                        && !self.check(TokenType::Outer)
4620                        && !self.check(TokenType::Full)
4621                        && !self.check(TokenType::Cross)
4622                        && !self.check(TokenType::Order)
4623                        && !self.check(TokenType::Group)
4624                        && !self.check(TokenType::Having)
4625                        && !self.check(TokenType::Limit)
4626                        && !self.check(TokenType::Union)
4627                        && !self.check(TokenType::Except)
4628                        && !self.check(TokenType::Intersect)
4629                        && !self.check(TokenType::As)
4630                        && !self.check(TokenType::Comma)
4631                        && !self.check(TokenType::RParen)
4632                        && !self.check(TokenType::With)
4633                        && !self.check(TokenType::Pivot)
4634                        && !self.check(TokenType::Unpivot)
4635                    {
4636                        self.skip();
4637                    }
4638                    let expr_text = self.tokens_to_sql_uppercased(start, self.current);
4639                    format!("FOR SYSTEM_TIME AS OF {}", expr_text)
4640                } else {
4641                    "FOR SYSTEM_TIME AS".to_string()
4642                }
4643            } else if self.match_token(TokenType::Between) {
4644                // BETWEEN expr AND expr
4645                let start = self.current;
4646                while !self.is_at_end() && !self.check(TokenType::And) {
4647                    self.skip();
4648                }
4649                let expr1_text = self.tokens_to_sql_uppercased(start, self.current);
4650                self.skip(); // consume AND
4651                let start2 = self.current;
4652                while !self.is_at_end()
4653                    && !self.check(TokenType::Semicolon)
4654                    && !self.check(TokenType::Where)
4655                    && !self.check(TokenType::Join)
4656                    && !self.check(TokenType::Left)
4657                    && !self.check(TokenType::Right)
4658                    && !self.check(TokenType::Inner)
4659                    && !self.check(TokenType::Outer)
4660                    && !self.check(TokenType::Full)
4661                    && !self.check(TokenType::Cross)
4662                    && !self.check(TokenType::Order)
4663                    && !self.check(TokenType::Group)
4664                    && !self.check(TokenType::Having)
4665                    && !self.check(TokenType::Limit)
4666                    && !self.check(TokenType::Union)
4667                    && !self.check(TokenType::Except)
4668                    && !self.check(TokenType::Intersect)
4669                    && !self.check(TokenType::As)
4670                    && !self.check(TokenType::Comma)
4671                    && !self.check(TokenType::RParen)
4672                    && !self.check(TokenType::With)
4673                    && !self.check(TokenType::Pivot)
4674                    && !self.check(TokenType::Unpivot)
4675                {
4676                    self.skip();
4677                }
4678                let expr2_text = self.tokens_to_sql_uppercased(start2, self.current);
4679                format!("FOR SYSTEM_TIME BETWEEN {} AND {}", expr1_text, expr2_text)
4680            } else if self.match_token(TokenType::From) {
4681                // FROM expr TO expr
4682                let start = self.current;
4683                while !self.is_at_end() && !self.check(TokenType::To) {
4684                    self.skip();
4685                }
4686                let expr1_text = self.tokens_to_sql_uppercased(start, self.current);
4687                self.skip(); // consume TO
4688                let start2 = self.current;
4689                while !self.is_at_end()
4690                    && !self.check(TokenType::Semicolon)
4691                    && !self.check(TokenType::Where)
4692                    && !self.check(TokenType::As)
4693                    && !self.check(TokenType::Comma)
4694                    && !self.check(TokenType::RParen)
4695                {
4696                    self.skip();
4697                }
4698                let expr2_text = self.tokens_to_sql_uppercased(start2, self.current);
4699                format!("FOR SYSTEM_TIME FROM {} TO {}", expr1_text, expr2_text)
4700            } else if self.check_identifier("CONTAINED") {
4701                self.skip(); // consume CONTAINED
4702                self.expect(TokenType::In)?;
4703                self.expect(TokenType::LParen)?;
4704                let start = self.current;
4705                let mut depth = 1;
4706                while !self.is_at_end() && depth > 0 {
4707                    if self.check(TokenType::LParen) {
4708                        depth += 1;
4709                    }
4710                    if self.check(TokenType::RParen) {
4711                        depth -= 1;
4712                        if depth == 0 {
4713                            break;
4714                        }
4715                    }
4716                    self.skip();
4717                }
4718                let inner_text = self.tokens_to_sql_uppercased(start, self.current);
4719                self.expect(TokenType::RParen)?;
4720                format!("FOR SYSTEM_TIME CONTAINED IN ({})", inner_text)
4721            } else if self.match_token(TokenType::All) {
4722                "FOR SYSTEM_TIME ALL".to_string()
4723            } else {
4724                "FOR SYSTEM_TIME".to_string()
4725            };
4726            if let Expression::Table(ref mut table) = expr {
4727                table.system_time = Some(system_time_str);
4728            }
4729        }
4730
4731        // Check for Presto/Trino time travel: FOR VERSION AS OF / FOR TIMESTAMP AS OF
4732        // Syntax: FOR VERSION AS OF <snapshot_id>
4733        //         FOR TIMESTAMP AS OF <timestamp_expr>
4734        if self.check(TokenType::For) && self.current + 1 < self.tokens.len() {
4735            let next_text = &self.tokens[self.current + 1].text;
4736            if next_text.eq_ignore_ascii_case("VERSION")
4737                || next_text.eq_ignore_ascii_case("TIMESTAMP")
4738            {
4739                self.skip(); // consume FOR
4740                let version_kind = self.advance().text.to_ascii_uppercase(); // consume VERSION or TIMESTAMP
4741
4742                // Expect AS OF
4743                if self.match_token(TokenType::As) && self.check_keyword_text("OF") {
4744                    self.skip(); // consume OF
4745
4746                    // Parse the expression value
4747                    if let Some(value_expr) = self.parse_bitwise()? {
4748                        let version = crate::expressions::Version {
4749                            this: Box::new(Expression::Identifier(Identifier::new(&version_kind))),
4750                            kind: "AS OF".to_string(),
4751                            expression: Some(Box::new(value_expr)),
4752                        };
4753                        if let Expression::Table(ref mut table) = expr {
4754                            table.version = Some(Box::new(version));
4755                        }
4756                    }
4757                }
4758            }
4759        }
4760
4761        // Check for Hive-style time travel: TIMESTAMP AS OF / VERSION AS OF (without FOR)
4762        // Syntax: TIMESTAMP AS OF <timestamp_expr>
4763        //         VERSION AS OF <snapshot_id>
4764        if self.current < self.tokens.len() {
4765            let current_text = &self.tokens[self.current].text;
4766            if (current_text.eq_ignore_ascii_case("TIMESTAMP")
4767                || current_text.eq_ignore_ascii_case("VERSION"))
4768                && self.current + 2 < self.tokens.len()
4769                && self.tokens[self.current + 1].token_type == TokenType::As
4770                && self.tokens[self.current + 2]
4771                    .text
4772                    .eq_ignore_ascii_case("OF")
4773            {
4774                let version_kind = self.advance().text.to_ascii_uppercase(); // consume TIMESTAMP or VERSION
4775                self.skip(); // consume AS
4776                self.skip(); // consume OF
4777
4778                // Parse the expression value
4779                if let Some(value_expr) = self.parse_bitwise()? {
4780                    let version = crate::expressions::Version {
4781                        this: Box::new(Expression::Identifier(Identifier::new(&version_kind))),
4782                        kind: "AS OF".to_string(),
4783                        expression: Some(Box::new(value_expr)),
4784                    };
4785                    if let Expression::Table(ref mut table) = expr {
4786                        table.version = Some(Box::new(version));
4787                    }
4788                }
4789            }
4790        }
4791
4792        // Check for MySQL PARTITION(p0, p1, ...) clause
4793        // Only supported by MySQL-compatible dialects (not generic dialect)
4794        let supports_partition_selection = matches!(
4795            self.config.dialect,
4796            Some(crate::dialects::DialectType::MySQL)
4797                | Some(crate::dialects::DialectType::SingleStore)
4798                | Some(crate::dialects::DialectType::Doris)
4799                | Some(crate::dialects::DialectType::StarRocks)
4800        );
4801        if supports_partition_selection && self.match_token(TokenType::Partition) {
4802            if self.match_token(TokenType::LParen) {
4803                let mut partitions = Vec::new();
4804                loop {
4805                    let partition_name = self.expect_identifier_or_keyword_with_quoted()?;
4806                    partitions.push(partition_name);
4807                    if !self.match_token(TokenType::Comma) {
4808                        break;
4809                    }
4810                }
4811                self.expect(TokenType::RParen)?;
4812                if let Expression::Table(ref mut table) = expr {
4813                    table.partitions = partitions;
4814                }
4815            }
4816        }
4817
4818        // Check for table-level TABLESAMPLE/SAMPLE: tbl TABLESAMPLE METHOD(size) or tbl SAMPLE ROW(0)
4819        // Snowflake supports both TABLESAMPLE and SAMPLE
4820        if self.check(TokenType::TableSample) || self.check(TokenType::Sample) {
4821            if let Some(sample) = self.parse_table_level_sample()? {
4822                if let Expression::Table(ref mut table) = expr {
4823                    table.table_sample = Some(Box::new(sample));
4824                } else {
4825                    // For non-Table expressions (subqueries, functions, etc.),
4826                    // wrap in TableSample expression node
4827                    expr = Expression::TableSample(Box::new(crate::expressions::TableSample {
4828                        this: Some(Box::new(expr)),
4829                        sample: Some(Box::new(sample)),
4830                        expressions: Vec::new(),
4831                        method: None,
4832                        bucket_numerator: None,
4833                        bucket_denominator: None,
4834                        bucket_field: None,
4835                        percent: None,
4836                        rows: None,
4837                        size: None,
4838                        seed: None,
4839                    }));
4840                }
4841            }
4842        }
4843
4844        // Check for TSQL table hints: WITH (TABLOCK, INDEX(myindex), ...)
4845        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
4846            if let Expression::Table(ref mut table) = expr {
4847                if let Some(hint_expr) = self.parse_table_hints()? {
4848                    // parse_table_hints returns a Tuple wrapping individual hint expressions.
4849                    // Extract the inner hints so we store them directly.
4850                    match hint_expr {
4851                        Expression::Tuple(tuple) => {
4852                            table.hints = tuple.expressions;
4853                        }
4854                        other => {
4855                            table.hints = vec![other];
4856                        }
4857                    }
4858                }
4859            }
4860        }
4861
4862        // Check for MySQL index hints: USE INDEX, IGNORE INDEX, FORCE INDEX
4863        if self.check_keyword_text("USE")
4864            || self.check(TokenType::Ignore)
4865            || self.check_keyword_text("FORCE")
4866        {
4867            // Peek ahead to see if next token after USE/IGNORE/FORCE is INDEX or KEY
4868            let next_idx = self.current + 1;
4869            let is_index_hint = next_idx < self.tokens.len() && {
4870                let next_text = &self.tokens[next_idx].text;
4871                next_text.eq_ignore_ascii_case("INDEX") || next_text.eq_ignore_ascii_case("KEY")
4872            };
4873            if is_index_hint {
4874                if let Expression::Table(ref mut table) = expr {
4875                    if let Some(hint_expr) = self.parse_table_hints()? {
4876                        match hint_expr {
4877                            Expression::Tuple(tuple) => {
4878                                table.hints = tuple.expressions;
4879                            }
4880                            other => {
4881                                table.hints = vec![other];
4882                            }
4883                        }
4884                    }
4885                }
4886            }
4887        }
4888
4889        // Check for SQLite INDEXED BY or NOT INDEXED table hints
4890        if self.check_identifier("INDEXED") {
4891            self.skip(); // consume INDEXED
4892            self.expect(TokenType::By)?;
4893            // Parse index name (can be qualified: schema.index)
4894            let first_part = self.expect_identifier_or_keyword()?;
4895            let index_name = if self.match_token(TokenType::Dot) {
4896                let second_part = self.expect_identifier_or_keyword()?;
4897                format!("{}.{}", first_part, second_part)
4898            } else {
4899                first_part
4900            };
4901            if let Expression::Table(ref mut table) = expr {
4902                table.hints.push(Expression::Identifier(Identifier {
4903                    name: format!("INDEXED BY {}", index_name),
4904                    quoted: false,
4905                    trailing_comments: Vec::new(),
4906                    span: None,
4907                }));
4908            }
4909        } else if self.check(TokenType::Not) && self.check_next_identifier("INDEXED") {
4910            self.skip(); // consume NOT
4911            self.skip(); // consume INDEXED
4912            if let Expression::Table(ref mut table) = expr {
4913                table.hints.push(Expression::Identifier(Identifier {
4914                    name: "NOT INDEXED".to_string(),
4915                    quoted: false,
4916                    trailing_comments: Vec::new(),
4917                    span: None,
4918                }));
4919            }
4920        }
4921
4922        // Check for PIVOT (can be followed by UNPIVOT)
4923        // Only treat as PIVOT clause when followed by ( — otherwise it's a table alias
4924        if self.check(TokenType::Pivot) && self.check_next(TokenType::LParen) {
4925            self.skip(); // consume PIVOT
4926            expr = self.parse_pivot(expr)?;
4927        }
4928        // Check for UNPIVOT (can follow PIVOT or be standalone)
4929        // Only treat as UNPIVOT clause when followed by (, INCLUDE, or EXCLUDE — otherwise it's a table alias
4930        if self.check(TokenType::Unpivot) && self.is_unpivot_clause_start() {
4931            self.skip(); // consume UNPIVOT
4932            expr = self.parse_unpivot(expr)?;
4933        }
4934        // Check for MATCH_RECOGNIZE
4935        else if self.check(TokenType::MatchRecognize)
4936            && !matches!(&expr, Expression::Pivot(_) | Expression::Unpivot(_))
4937        {
4938            self.skip();
4939            expr = self.parse_match_recognize(Some(expr))?;
4940        }
4941
4942        // Check for alias
4943        if self.match_token(TokenType::As) {
4944            // Handle AS (col1, col2) without alias name - used by POSEXPLODE etc.
4945            if self.check(TokenType::LParen) {
4946                self.skip(); // consume LParen
4947                let mut column_aliases = Vec::new();
4948                loop {
4949                    if self.check(TokenType::RParen) {
4950                        break;
4951                    }
4952                    column_aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
4953                    if !self.match_token(TokenType::Comma) {
4954                        break;
4955                    }
4956                }
4957                self.expect(TokenType::RParen)?;
4958                expr = Expression::Alias(Box::new(Alias {
4959                    this: expr,
4960                    alias: Identifier::new(String::new()),
4961                    column_aliases,
4962                    pre_alias_comments: Vec::new(),
4963                    trailing_comments: Vec::new(),
4964                    inferred_type: None,
4965                }));
4966            } else {
4967                let alias_ident_parsed = self.expect_identifier_or_alias_keyword_with_quoted()?;
4968                let alias = alias_ident_parsed.name;
4969                let alias_is_quoted = alias_ident_parsed.quoted;
4970                let make_alias_ident = |name: String| -> Identifier {
4971                    if alias_is_quoted {
4972                        Identifier::quoted(name)
4973                    } else {
4974                        Identifier::new(name)
4975                    }
4976                };
4977                // Check for column aliases: AS t(c1, c2) or AS t(c1 type1, c2 type2) for table functions
4978                if self.match_token(TokenType::LParen) {
4979                    // Check if this is typed column definitions (for table functions like JSON_TO_RECORDSET)
4980                    // by looking ahead: if we see identifier followed by another identifier/type (not comma/rparen),
4981                    // it's typed columns
4982                    let has_typed_columns = self.check_typed_column_list();
4983
4984                    if has_typed_columns {
4985                        // Parse typed column definitions like: (col1 type1, col2 type2)
4986                        let mut typed_cols = Vec::new();
4987                        loop {
4988                            if self.check(TokenType::RParen) {
4989                                break;
4990                            }
4991                            // Parse column name (can be quoted)
4992                            let col_name = self.expect_identifier_or_keyword_with_quoted()?;
4993                            // Parse column type
4994                            let col_type = self.parse_data_type()?;
4995                            // Create ColumnDef expression, preserving the quoted status
4996                            let mut col_def = ColumnDef::new(col_name.name.clone(), col_type);
4997                            col_def.name = col_name;
4998                            typed_cols.push(Expression::ColumnDef(Box::new(col_def)));
4999
5000                            if !self.match_token(TokenType::Comma) {
5001                                break;
5002                            }
5003                        }
5004                        self.expect(TokenType::RParen)?;
5005
5006                        // Create TableAlias with typed columns
5007                        let table_alias = Expression::TableAlias(Box::new(TableAlias {
5008                            this: Some(Box::new(Expression::Identifier(make_alias_ident(alias)))),
5009                            columns: typed_cols,
5010                        }));
5011
5012                        // Wrap function with TableAlias using Tuple pattern (like ROWS FROM)
5013                        expr = Expression::Tuple(Box::new(Tuple {
5014                            expressions: vec![expr, table_alias],
5015                        }));
5016                    } else {
5017                        // Parse simple column aliases: (c1, c2, ...)
5018                        // Use expect_identifier_or_keyword to allow keywords like KEY, INDEX, VALUE as column aliases
5019                        let mut aliases = Vec::new();
5020                        loop {
5021                            if self.check(TokenType::RParen) {
5022                                break;
5023                            }
5024                            aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
5025                            if !self.match_token(TokenType::Comma) {
5026                                break;
5027                            }
5028                        }
5029                        self.expect(TokenType::RParen)?;
5030
5031                        expr = match expr {
5032                            Expression::Table(mut t) => {
5033                                t.alias = Some(make_alias_ident(alias));
5034                                t.alias_explicit_as = true;
5035                                t.column_aliases = aliases;
5036                                Expression::Table(t)
5037                            }
5038                            Expression::Subquery(mut s) => {
5039                                s.alias = Some(make_alias_ident(alias));
5040                                s.column_aliases = aliases;
5041                                Expression::Subquery(s)
5042                            }
5043                            Expression::Pivot(mut p) => {
5044                                p.alias = Some(make_alias_ident(alias));
5045                                Expression::Pivot(p)
5046                            }
5047                            Expression::Unpivot(mut u) => {
5048                                u.alias = Some(make_alias_ident(alias));
5049                                Expression::Unpivot(u)
5050                            }
5051                            Expression::MatchRecognize(mut mr) => {
5052                                mr.alias = Some(make_alias_ident(alias));
5053                                mr.alias_explicit_as = true;
5054                                Expression::MatchRecognize(mr)
5055                            }
5056                            Expression::JoinedTable(mut jt) => {
5057                                jt.alias = Some(make_alias_ident(alias));
5058                                Expression::JoinedTable(jt)
5059                            }
5060                            _ => Expression::Alias(Box::new(Alias {
5061                                this: expr,
5062                                alias: make_alias_ident(alias),
5063                                column_aliases: aliases,
5064                                pre_alias_comments: Vec::new(),
5065                                trailing_comments: Vec::new(),
5066                                inferred_type: None,
5067                            })),
5068                        };
5069                    }
5070                } else {
5071                    // No column aliases, just simple alias
5072                    let default_column_aliases = if matches!(
5073                        self.config.dialect,
5074                        Some(crate::dialects::DialectType::ClickHouse)
5075                    ) && matches!(&expr, Expression::Function(func) if func.name.eq_ignore_ascii_case("generate_series"))
5076                    {
5077                        vec![Identifier::new("generate_series")]
5078                    } else {
5079                        Vec::new()
5080                    };
5081                    expr = match expr {
5082                        Expression::Table(mut t) => {
5083                            t.alias = Some(make_alias_ident(alias));
5084                            t.alias_explicit_as = true;
5085                            t.column_aliases = Vec::new();
5086                            Expression::Table(t)
5087                        }
5088                        Expression::Subquery(mut s) => {
5089                            s.alias = Some(make_alias_ident(alias));
5090                            s.column_aliases = Vec::new();
5091                            Expression::Subquery(s)
5092                        }
5093                        Expression::Pivot(mut p) => {
5094                            p.alias = Some(make_alias_ident(alias));
5095                            Expression::Pivot(p)
5096                        }
5097                        Expression::Unpivot(mut u) => {
5098                            u.alias = Some(make_alias_ident(alias));
5099                            Expression::Unpivot(u)
5100                        }
5101                        Expression::MatchRecognize(mut mr) => {
5102                            mr.alias = Some(make_alias_ident(alias));
5103                            mr.alias_explicit_as = true;
5104                            Expression::MatchRecognize(mr)
5105                        }
5106                        Expression::JoinedTable(mut jt) => {
5107                            jt.alias = Some(make_alias_ident(alias));
5108                            Expression::JoinedTable(jt)
5109                        }
5110                        _ => Expression::Alias(Box::new(Alias {
5111                            this: expr,
5112                            alias: make_alias_ident(alias),
5113                            column_aliases: default_column_aliases,
5114                            pre_alias_comments: Vec::new(),
5115                            trailing_comments: Vec::new(),
5116                            inferred_type: None,
5117                        })),
5118                    };
5119                }
5120            } // close the else for AS (col1, col2) handling
5121        } else if (self.check(TokenType::QuotedIdentifier)
5122            || (self.check(TokenType::Var) && !self.check_keyword() && !self.check_identifier("MATCH_CONDITION")
5123                && !(self.check_identifier("ARRAY") && self.check_next(TokenType::Join)
5124                     && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
5125                // TSQL: OPTION(LABEL = 'foo') is a query hint, not an alias
5126                && !(self.check_identifier("OPTION") && self.check_next(TokenType::LParen))
5127                // MySQL: LOCK IN SHARE MODE is a locking clause, not an alias
5128                && !(self.check_identifier("LOCK") && self.check_next(TokenType::In))
5129                // ClickHouse: PARALLEL WITH is a statement separator, not a table alias
5130                && !(self.check_identifier("PARALLEL") && self.check_next(TokenType::With)
5131                     && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)))
5132                // DuckDB: POSITIONAL JOIN is a join method, not a table alias
5133                && !(self.check_identifier("POSITIONAL") && self.check_next(TokenType::Join))))
5134            || self.is_command_keyword_as_alias()
5135            // ClickHouse: allow FIRST/LAST as implicit table aliases
5136            // (they're keywords used in NULLS FIRST/LAST but also valid as identifiers)
5137            || (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse))
5138                && (self.check(TokenType::First) || self.check(TokenType::Last)))
5139            // PIVOT/UNPIVOT can be table aliases when not followed by clause-starting tokens
5140            || (self.check(TokenType::Pivot) && !self.check_next(TokenType::LParen))
5141            || (self.check(TokenType::Unpivot) && !self.is_unpivot_clause_start())
5142            // PARTITION can be a table alias when the dialect doesn't support partition selection
5143            || (self.check(TokenType::Partition) && !matches!(
5144                self.config.dialect,
5145                Some(crate::dialects::DialectType::MySQL)
5146                | Some(crate::dialects::DialectType::SingleStore)
5147                | Some(crate::dialects::DialectType::Doris)
5148                | Some(crate::dialects::DialectType::StarRocks)
5149            ))
5150            || (self.check(TokenType::Window) && {
5151                // WINDOW can be a table alias if NOT followed by an identifier (window definition)
5152                let next_pos = self.current + 1;
5153                next_pos >= self.tokens.len()
5154                    || (self.tokens[next_pos].token_type != TokenType::Var
5155                        && self.tokens[next_pos].token_type != TokenType::Identifier)
5156            })
5157        {
5158            // Implicit alias (but not MATCH_CONDITION which is a join condition keyword)
5159            // Also allow command keywords (GET, PUT, etc.) and WINDOW (when not a clause) as implicit table aliases
5160            let is_keyword_alias = self.peek().token_type.is_keyword();
5161            let is_quoted_alias = self.peek().token_type == TokenType::QuotedIdentifier;
5162            let alias = self.advance().text.clone();
5163            // Check for column aliases: t(c1, c2)
5164            // Use expect_identifier_or_keyword to allow keywords like KEY, INDEX, VALUE as column aliases
5165            let mut column_aliases = if self.match_token(TokenType::LParen) {
5166                let mut aliases = Vec::new();
5167                loop {
5168                    aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
5169                    if !self.match_token(TokenType::Comma) {
5170                        break;
5171                    }
5172                }
5173                self.expect(TokenType::RParen)?;
5174                aliases
5175            } else {
5176                Vec::new()
5177            };
5178            if column_aliases.is_empty()
5179                && matches!(
5180                    self.config.dialect,
5181                    Some(crate::dialects::DialectType::ClickHouse)
5182                )
5183                && matches!(&expr, Expression::Function(func) if func.name.eq_ignore_ascii_case("generate_series"))
5184            {
5185                column_aliases = vec![Identifier::new("generate_series")];
5186            }
5187            let make_alias_ident = |name: String| -> Identifier {
5188                if is_quoted_alias {
5189                    Identifier::quoted(name)
5190                } else {
5191                    Identifier::new(name)
5192                }
5193            };
5194            expr = match expr {
5195                Expression::Table(mut t) => {
5196                    t.alias = Some(make_alias_ident(alias));
5197                    t.alias_explicit_as = is_keyword_alias;
5198                    t.column_aliases = column_aliases;
5199                    Expression::Table(t)
5200                }
5201                Expression::Subquery(mut s) => {
5202                    s.alias = Some(make_alias_ident(alias));
5203                    s.column_aliases = column_aliases;
5204                    Expression::Subquery(s)
5205                }
5206                Expression::Pivot(mut p) => {
5207                    p.alias = Some(make_alias_ident(alias));
5208                    Expression::Pivot(p)
5209                }
5210                Expression::Unpivot(mut u) => {
5211                    u.alias = Some(make_alias_ident(alias));
5212                    Expression::Unpivot(u)
5213                }
5214                Expression::MatchRecognize(mut mr) => {
5215                    mr.alias = Some(make_alias_ident(alias));
5216                    Expression::MatchRecognize(mr)
5217                }
5218                Expression::JoinedTable(mut jt) => {
5219                    jt.alias = Some(make_alias_ident(alias));
5220                    Expression::JoinedTable(jt)
5221                }
5222                _ => Expression::Alias(Box::new(Alias {
5223                    this: expr,
5224                    alias: make_alias_ident(alias),
5225                    column_aliases,
5226                    pre_alias_comments: Vec::new(),
5227                    trailing_comments: Vec::new(),
5228                    inferred_type: None,
5229                })),
5230            };
5231        }
5232
5233        // ClickHouse: subquery column alias list without alias name: FROM (...) (c0, c1)
5234        if matches!(
5235            self.config.dialect,
5236            Some(crate::dialects::DialectType::ClickHouse)
5237        ) && self.check(TokenType::LParen)
5238            && matches!(&expr, Expression::Subquery(s) if s.alias.is_none())
5239        {
5240            // Lookahead: check if this is (identifier, identifier, ...) — column alias list
5241            let mut look = self.current + 1;
5242            let mut is_col_list = true;
5243            let mut col_count = 0;
5244            loop {
5245                if look >= self.tokens.len() {
5246                    is_col_list = false;
5247                    break;
5248                }
5249                let tt = self.tokens[look].token_type;
5250                if tt == TokenType::Identifier
5251                    || tt == TokenType::Var
5252                    || tt == TokenType::QuotedIdentifier
5253                    || tt.is_keyword()
5254                {
5255                    col_count += 1;
5256                    look += 1;
5257                } else {
5258                    is_col_list = false;
5259                    break;
5260                }
5261                if look >= self.tokens.len() {
5262                    is_col_list = false;
5263                    break;
5264                }
5265                if self.tokens[look].token_type == TokenType::Comma {
5266                    look += 1;
5267                } else if self.tokens[look].token_type == TokenType::RParen {
5268                    break;
5269                } else {
5270                    is_col_list = false;
5271                    break;
5272                }
5273            }
5274            if is_col_list && col_count >= 1 {
5275                self.skip(); // consume LParen
5276                let mut aliases = Vec::new();
5277                loop {
5278                    aliases.push(Identifier::new(self.advance().text.clone()));
5279                    if !self.match_token(TokenType::Comma) {
5280                        break;
5281                    }
5282                }
5283                self.expect(TokenType::RParen)?;
5284                if let Expression::Subquery(ref mut s) = expr {
5285                    s.column_aliases = aliases;
5286                }
5287            }
5288        }
5289
5290        // ClickHouse FINAL modifier: table [AS alias] FINAL
5291        if matches!(
5292            self.config.dialect,
5293            Some(crate::dialects::DialectType::ClickHouse)
5294        ) && self.match_token(TokenType::Final)
5295        {
5296            if let Expression::Table(ref mut table) = expr {
5297                table.final_ = true;
5298            }
5299        }
5300
5301        // Check for SQLite INDEXED BY after alias: t AS t INDEXED BY idx
5302        if self.check_identifier("INDEXED") {
5303            self.skip(); // consume INDEXED
5304            self.expect(TokenType::By)?;
5305            let first_part = self.expect_identifier_or_keyword()?;
5306            let index_name = if self.match_token(TokenType::Dot) {
5307                let second_part = self.expect_identifier_or_keyword()?;
5308                format!("{}.{}", first_part, second_part)
5309            } else {
5310                first_part
5311            };
5312            if let Expression::Table(ref mut table) = expr {
5313                table.hints.push(Expression::Identifier(Identifier {
5314                    name: format!("INDEXED BY {}", index_name),
5315                    quoted: false,
5316                    trailing_comments: Vec::new(),
5317                    span: None,
5318                }));
5319            }
5320        }
5321
5322        // Check for MySQL index hints after alias: t e USE INDEX (idx), t AS a IGNORE INDEX (idx)
5323        if self.check_keyword_text("USE")
5324            || self.check(TokenType::Ignore)
5325            || self.check_keyword_text("FORCE")
5326        {
5327            let next_idx = self.current + 1;
5328            let is_index_hint = next_idx < self.tokens.len() && {
5329                let next_text = &self.tokens[next_idx].text;
5330                next_text.eq_ignore_ascii_case("INDEX") || next_text.eq_ignore_ascii_case("KEY")
5331            };
5332            if is_index_hint {
5333                if let Expression::Table(ref mut table) = expr {
5334                    if let Some(hint_expr) = self.parse_table_hints()? {
5335                        match hint_expr {
5336                            Expression::Tuple(tuple) => {
5337                                table.hints = tuple.expressions;
5338                            }
5339                            other => {
5340                                table.hints = vec![other];
5341                            }
5342                        }
5343                    }
5344                }
5345            }
5346        }
5347
5348        // Check for PIVOT/UNPIVOT after alias (some dialects allow this order)
5349        // Only treat as PIVOT/UNPIVOT clause when followed by ( — otherwise it's a table alias
5350        if self.check(TokenType::Pivot) && self.check_next(TokenType::LParen) {
5351            self.skip(); // consume PIVOT
5352            expr = self.parse_pivot(expr)?;
5353        } else if self.check(TokenType::Unpivot) && self.is_unpivot_clause_start() {
5354            self.skip(); // consume UNPIVOT
5355            expr = self.parse_unpivot(expr)?;
5356        }
5357
5358        // Check for Redshift AT index clause for array unnesting
5359        // Syntax: table_alias.array_column AS element_alias AT index_alias
5360        // e.g., c.c_orders AS orders AT index
5361        // https://docs.aws.amazon.com/redshift/latest/dg/query-super.html
5362        if self.match_identifier("AT") {
5363            let index_alias = self.expect_identifier_or_keyword()?;
5364            // Convert the table expression to a column for AtIndex
5365            let column_expr = match expr {
5366                Expression::Table(t) => {
5367                    // Convert Table to Column reference
5368                    // For c.c_orders, table=c, name=c_orders -> column name should be c.c_orders
5369                    let mut parts = Vec::new();
5370                    if let Some(cat) = t.catalog {
5371                        parts.push(cat.name);
5372                    }
5373                    if let Some(schema) = t.schema {
5374                        parts.push(schema.name);
5375                    }
5376                    parts.push(t.name.name);
5377                    let col_name = parts.join(".");
5378                    let alias_expr = if let Some(alias) = t.alias {
5379                        Expression::Alias(Box::new(Alias {
5380                            this: Expression::boxed_column(Column {
5381                                name: Identifier::new(&col_name),
5382                                table: None,
5383                                join_mark: false,
5384                                trailing_comments: Vec::new(),
5385                                span: None,
5386                                inferred_type: None,
5387                            }),
5388                            alias,
5389                            column_aliases: t.column_aliases,
5390                            pre_alias_comments: Vec::new(),
5391                            trailing_comments: t.trailing_comments,
5392                            inferred_type: None,
5393                        }))
5394                    } else {
5395                        Expression::boxed_column(Column {
5396                            name: Identifier::new(&col_name),
5397                            table: None,
5398                            join_mark: false,
5399                            trailing_comments: t.trailing_comments,
5400                            span: None,
5401                            inferred_type: None,
5402                        })
5403                    };
5404                    alias_expr
5405                }
5406                other => other, // Keep as is for non-table expressions
5407            };
5408            expr = Expression::AtIndex(Box::new(AtIndex {
5409                this: Box::new(column_expr),
5410                expression: Box::new(Expression::Identifier(Identifier::new(index_alias))),
5411            }));
5412        }
5413
5414        // Check for TABLESAMPLE/SAMPLE after alias (Snowflake ALIAS_POST_TABLESAMPLE)
5415        // e.g., table2 AS t2 TABLESAMPLE BERNOULLI (50), table2 AS t2 SAMPLE ROW (0)
5416        if self.check(TokenType::TableSample) || self.check(TokenType::Sample) {
5417            if let Some(sample) = self.parse_table_level_sample()? {
5418                // Capture trailing comments after the SAMPLE clause (e.g., -- 25% of rows in table1)
5419                let post_sample_comments = self.previous_trailing_comments().to_vec();
5420                if let Expression::Table(ref mut table) = expr {
5421                    table.table_sample = Some(Box::new(sample));
5422                    if !post_sample_comments.is_empty() {
5423                        table.trailing_comments.extend(post_sample_comments);
5424                    }
5425                } else {
5426                    // For non-Table expressions, wrap in TableSample expression node
5427                    expr = Expression::TableSample(Box::new(crate::expressions::TableSample {
5428                        this: Some(Box::new(expr)),
5429                        sample: Some(Box::new(sample)),
5430                        expressions: Vec::new(),
5431                        method: None,
5432                        bucket_numerator: None,
5433                        bucket_denominator: None,
5434                        bucket_field: None,
5435                        percent: None,
5436                        rows: None,
5437                        size: None,
5438                        seed: None,
5439                    }));
5440                }
5441            }
5442        }
5443
5444        // Apply PostgreSQL ONLY modifier if present
5445        if has_only {
5446            if let Expression::Table(ref mut table) = expr {
5447                table.only = true;
5448            }
5449        }
5450
5451        // BigQuery: FOR SYSTEM_TIME AS OF after alias
5452        // e.g., FROM foo AS t0 FOR SYSTEM_TIME AS OF '2026-01-01'
5453        if self.check(TokenType::For)
5454            && self.current + 1 < self.tokens.len()
5455            && self.tokens[self.current + 1]
5456                .text
5457                .eq_ignore_ascii_case("SYSTEM_TIME")
5458        {
5459            self.skip(); // consume FOR
5460            self.skip(); // consume SYSTEM_TIME
5461            if self.match_token(TokenType::As) && self.check_keyword_text("OF") {
5462                self.skip(); // consume OF
5463                let start = self.current;
5464                // Collect expression tokens until clause boundary
5465                while !self.is_at_end()
5466                    && !self.check(TokenType::Semicolon)
5467                    && !self.check(TokenType::Where)
5468                    && !self.check(TokenType::Join)
5469                    && !self.check(TokenType::Left)
5470                    && !self.check(TokenType::Right)
5471                    && !self.check(TokenType::Inner)
5472                    && !self.check(TokenType::Outer)
5473                    && !self.check(TokenType::Full)
5474                    && !self.check(TokenType::Cross)
5475                    && !self.check(TokenType::Order)
5476                    && !self.check(TokenType::Group)
5477                    && !self.check(TokenType::Having)
5478                    && !self.check(TokenType::Limit)
5479                    && !self.check(TokenType::Union)
5480                    && !self.check(TokenType::Except)
5481                    && !self.check(TokenType::Intersect)
5482                    && !self.check(TokenType::Comma)
5483                    && !self.check(TokenType::RParen)
5484                {
5485                    self.skip();
5486                }
5487                let expr_text = self.tokens_to_sql(start, self.current);
5488                let system_time_str = format!("FOR SYSTEM_TIME AS OF {}", expr_text);
5489                if let Expression::Table(ref mut table) = expr {
5490                    table.system_time = Some(system_time_str);
5491                }
5492            }
5493        }
5494
5495        // BigQuery INFORMATION_SCHEMA handling
5496        // When INFORMATION_SCHEMA is part of a table reference, merge it with the table name
5497        // into a single quoted identifier and auto-add an alias if not present
5498        if matches!(
5499            self.config.dialect,
5500            Some(crate::dialects::DialectType::BigQuery)
5501        ) {
5502            if let Expression::Table(ref mut table) = expr {
5503                // Case 1: Single quoted identifier containing INFORMATION_SCHEMA (e.g., `proj.dataset.INFORMATION_SCHEMA.SOME_VIEW`)
5504                // Add an alias that is the same as the table name (only if no alias)
5505                if table.schema.is_none() && table.catalog.is_none() && table.alias.is_none() {
5506                    let name_upper = table.name.name.to_ascii_uppercase();
5507                    if name_upper.contains("INFORMATION_SCHEMA.") {
5508                        // Set alias to be the full quoted table name
5509                        table.alias = Some(table.name.clone());
5510                        table.alias_explicit_as = true;
5511                    }
5512                }
5513                // Case 2: Multi-part name where schema part is INFORMATION_SCHEMA
5514                // e.g., region_or_dataset.INFORMATION_SCHEMA.TABLES -> region_or_dataset.`INFORMATION_SCHEMA.TABLES` AS TABLES
5515                // e.g., proj.region_or_dataset.INFORMATION_SCHEMA.TABLES -> proj.region_or_dataset.`INFORMATION_SCHEMA.TABLES` AS TABLES
5516                // This applies even if an alias is already set (we still need to merge the parts)
5517                else if let Some(ref schema) = table.schema {
5518                    if schema.name.eq_ignore_ascii_case("INFORMATION_SCHEMA") {
5519                        // Merge schema (INFORMATION_SCHEMA) with table name into a single quoted identifier
5520                        let merged_name = format!("{}.{}", schema.name, table.name.name);
5521                        let original_table_name = table.name.name.clone();
5522
5523                        // Set alias to original table name (TABLES, VIEWS, etc.) only if no alias exists
5524                        if table.alias.is_none() {
5525                            table.alias = Some(Identifier::new(original_table_name));
5526                            table.alias_explicit_as = true;
5527                        }
5528
5529                        // Create new quoted identifier
5530                        table.name = Identifier {
5531                            name: merged_name,
5532                            quoted: true,
5533                            trailing_comments: Vec::new(),
5534                            span: None,
5535                        };
5536
5537                        // Shift: schema becomes catalog, catalog becomes None or stays
5538                        table.schema = table.catalog.take();
5539                        // catalog is now None
5540                    }
5541                }
5542            }
5543        }
5544
5545        Ok(expr)
5546    }
5547
5548    /// Parse standard PIVOT clause (in FROM clause)
5549    /// PIVOT(agg_func [AS alias], ... FOR column IN (value [AS alias], ...) [GROUP BY ...])
5550    fn parse_pivot(&mut self, source: Expression) -> Result<Expression> {
5551        self.expect(TokenType::LParen)?;
5552
5553        // Parse aggregation functions (comma-separated, may have aliases)
5554        // Stop when we see FOR keyword
5555        // Use parse_primary() to handle keyword function names like FIRST, LAST
5556        let mut expressions = Vec::new();
5557        loop {
5558            if self.check(TokenType::For) || self.check(TokenType::RParen) {
5559                break;
5560            }
5561            // Parse the aggregation expression using parse_primary (handles keyword functions)
5562            let func = self.parse_primary()?;
5563            // Check for alias (AS alias or just identifier after function)
5564            let expr = if self.match_token(TokenType::As) {
5565                // AS alias
5566                let alias_name = self.expect_identifier_or_keyword()?;
5567                Expression::Alias(Box::new(Alias::new(func, Identifier::new(alias_name))))
5568            } else if !self.check(TokenType::Comma)
5569                && !self.check(TokenType::For)
5570                && !self.check(TokenType::RParen)
5571            {
5572                // Implicit alias (no AS keyword): SUM(b) d
5573                if let Some(id) = self.parse_id_var()? {
5574                    let alias_name = match &id {
5575                        Expression::Identifier(ident) => ident.name.clone(),
5576                        Expression::Column(col) => col.name.name.clone(),
5577                        _ => String::new(),
5578                    };
5579                    if !alias_name.is_empty() {
5580                        Expression::Alias(Box::new(Alias::new(func, Identifier::new(alias_name))))
5581                    } else {
5582                        func
5583                    }
5584                } else {
5585                    func
5586                }
5587            } else {
5588                func
5589            };
5590            expressions.push(expr);
5591            if !self.match_token(TokenType::Comma) {
5592                break;
5593            }
5594            // After consuming comma, if next is FOR, break (comma before FOR is optional/dropped)
5595            if self.check(TokenType::For) {
5596                break;
5597            }
5598        }
5599
5600        // FOR column IN (values)
5601        self.expect(TokenType::For)?;
5602
5603        let mut fields = Vec::new();
5604        loop {
5605            let field = self.parse_standard_pivot_in()?;
5606            fields.push(field);
5607
5608            // Check for additional FOR clauses (rare but possible)
5609            if !self.match_token(TokenType::For) {
5610                break;
5611            }
5612        }
5613
5614        // Handle Snowflake's DEFAULT ON NULL (default_value) clause
5615        let default_on_null = if self.match_text_seq(&["DEFAULT", "ON", "NULL"]) {
5616            if self.match_token(TokenType::LParen) {
5617                let val = self.parse_expression()?;
5618                self.expect(TokenType::RParen)?;
5619                Some(Box::new(val))
5620            } else {
5621                None
5622            }
5623        } else {
5624            None
5625        };
5626
5627        // Parse optional GROUP BY inside PIVOT parens
5628        let group = self.parse_group()?;
5629
5630        self.expect(TokenType::RParen)?;
5631
5632        Ok(Expression::Pivot(Box::new(Pivot {
5633            this: source,
5634            expressions,
5635            fields,
5636            using: Vec::new(),
5637            group: group.map(Box::new),
5638            unpivot: false,
5639            into: None,
5640            alias: None,
5641            include_nulls: None,
5642            default_on_null,
5643            with: None,
5644        })))
5645    }
5646
5647    /// Parse FOR column IN (...) part of standard PIVOT
5648    fn parse_standard_pivot_in(&mut self) -> Result<Expression> {
5649        // Parse the column being pivoted
5650        let column = self.parse_primary()?;
5651
5652        // IN keyword
5653        self.expect(TokenType::In)?;
5654
5655        // IN values - can be parenthesized or bare identifier
5656        if self.match_token(TokenType::LParen) {
5657            // Check for ANY keyword
5658            let in_exprs = if self.match_text_seq(&["ANY"]) {
5659                let order = self.parse_order()?;
5660                vec![Expression::PivotAny(Box::new(PivotAny {
5661                    this: order.map(Box::new),
5662                }))]
5663            } else {
5664                // Parse comma-separated values with optional aliases
5665                let mut vals = Vec::new();
5666                loop {
5667                    if self.check(TokenType::RParen) {
5668                        break;
5669                    }
5670                    if let Some(val) = self.parse_select_or_expression()? {
5671                        // Check for alias - alias can be an identifier or an expression
5672                        // (e.g., 'PREFIX ' || CHR(38) || ' SUFFIX' in Oracle)
5673                        let val = if self.match_token(TokenType::As) {
5674                            // Parse the alias as an expression (not just an identifier)
5675                            // This allows for string concatenation aliases
5676                            let alias_expr = self.parse_bitwise()?.ok_or_else(|| {
5677                                self.parse_error(
5678                                    "Expected expression after AS in PIVOT/UNPIVOT IN clause",
5679                                )
5680                            })?;
5681                            Expression::PivotAlias(Box::new(PivotAlias {
5682                                this: val,
5683                                alias: alias_expr,
5684                            }))
5685                        } else {
5686                            val
5687                        };
5688                        vals.push(val);
5689                    }
5690                    if !self.match_token(TokenType::Comma) {
5691                        break;
5692                    }
5693                }
5694                vals
5695            };
5696            self.expect(TokenType::RParen)?;
5697            Ok(Expression::In(Box::new(In {
5698                this: column,
5699                expressions: in_exprs,
5700                query: None,
5701                not: false,
5702                global: false,
5703                unnest: None,
5704                is_field: false,
5705            })))
5706        } else {
5707            // Bare identifier: FOR foo IN y_enum (no parentheses)
5708            // Store in query field to distinguish from parenthesized IN
5709            let field_id = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
5710            Ok(Expression::In(Box::new(In {
5711                this: column,
5712                expressions: Vec::new(),
5713                query: Some(field_id),
5714                not: false,
5715                global: false,
5716                unnest: None,
5717                is_field: true,
5718            })))
5719        }
5720    }
5721
5722    /// Parse UNPIVOT clause
5723    /// UNPIVOT (value_column FOR name_column IN (col1, col2, ...))
5724    /// UNPIVOT ((col1, col2) FOR name_column IN (col1, col2, ...))
5725    /// UNPIVOT INCLUDE NULLS (value_column FOR name_column IN (...))
5726    /// UNPIVOT EXCLUDE NULLS (value_column FOR name_column IN (...))
5727    fn parse_unpivot(&mut self, source: Expression) -> Result<Expression> {
5728        // Check for optional INCLUDE NULLS or EXCLUDE NULLS
5729        let include_nulls = if self.match_text_seq(&["INCLUDE", "NULLS"]) {
5730            Some(true)
5731        } else if self.match_text_seq(&["EXCLUDE", "NULLS"]) {
5732            Some(false)
5733        } else {
5734            None
5735        };
5736
5737        self.expect(TokenType::LParen)?;
5738
5739        // Value column(s) - can be identifier or (col1, col2, ...)
5740        // Allow keywords as identifiers (e.g., "values" is a common column name in UNPIVOT)
5741        let (value_column, value_column_parenthesized, extra_value_columns) =
5742            if self.match_token(TokenType::LParen) {
5743                // Parenthesized value column(s)
5744                let col = self.expect_identifier_or_keyword()?;
5745                let mut extra_cols = Vec::new();
5746                while self.match_token(TokenType::Comma) {
5747                    extra_cols.push(Identifier::new(self.expect_identifier_or_keyword()?));
5748                }
5749                self.expect(TokenType::RParen)?;
5750                (Identifier::new(col), true, extra_cols)
5751            } else {
5752                (
5753                    Identifier::new(self.expect_identifier_or_keyword()?),
5754                    false,
5755                    Vec::new(),
5756                )
5757            };
5758
5759        // FOR name_column
5760        self.expect(TokenType::For)?;
5761        let name_column = Identifier::new(self.expect_identifier_or_keyword()?);
5762
5763        // IN (columns with optional aliases)
5764        // Format: col1 [AS alias1], col2 [AS alias2], ...
5765        // Or tuple format: (col1, col2) [AS alias1], (col3, col4) [AS alias2], ...
5766        // Aliases can be expressions like 'PREFIX ' || CHR(38) || ' SUFFIX'
5767        self.expect(TokenType::In)?;
5768        self.expect(TokenType::LParen)?;
5769        let columns = {
5770            let mut cols = Vec::new();
5771            loop {
5772                if self.check(TokenType::RParen) {
5773                    break;
5774                }
5775                // Check if this is a tuple of columns: (col1, col2)
5776                let col_expr = if self.check(TokenType::LParen) {
5777                    // Could be a tuple of columns for multi-value unpivot
5778                    let saved = self.current;
5779                    self.skip(); // consume (
5780                                 // Try parsing as identifier list (tuple of columns)
5781                    let mut tuple_cols = Vec::new();
5782                    let first = self.expect_identifier_or_keyword();
5783                    if let Ok(first_id) = first {
5784                        tuple_cols.push(Expression::column(first_id));
5785                        while self.match_token(TokenType::Comma) {
5786                            if let Ok(id) = self.expect_identifier_or_keyword() {
5787                                tuple_cols.push(Expression::column(id));
5788                            } else {
5789                                break;
5790                            }
5791                        }
5792                        if self.match_token(TokenType::RParen) && tuple_cols.len() > 1 {
5793                            // Successful tuple parse
5794                            Some(Expression::Tuple(Box::new(Tuple {
5795                                expressions: tuple_cols,
5796                            })))
5797                        } else {
5798                            // Not a tuple, backtrack
5799                            self.current = saved;
5800                            self.parse_select_or_expression()?
5801                        }
5802                    } else {
5803                        // Not an identifier, backtrack
5804                        self.current = saved;
5805                        self.parse_select_or_expression()?
5806                    }
5807                } else {
5808                    self.parse_select_or_expression()?
5809                };
5810
5811                if let Some(col) = col_expr {
5812                    // Check for alias
5813                    let col = if self.match_token(TokenType::As) {
5814                        // Parse the alias as an expression (allows string concatenation)
5815                        let alias_expr = self.parse_bitwise()?.ok_or_else(|| {
5816                            self.parse_error("Expected expression after AS in UNPIVOT IN clause")
5817                        })?;
5818                        Expression::PivotAlias(Box::new(PivotAlias {
5819                            this: col,
5820                            alias: alias_expr,
5821                        }))
5822                    } else {
5823                        col
5824                    };
5825                    cols.push(col);
5826                }
5827                if !self.match_token(TokenType::Comma) {
5828                    break;
5829                }
5830            }
5831            cols
5832        };
5833        self.expect(TokenType::RParen)?;
5834
5835        self.expect(TokenType::RParen)?;
5836
5837        Ok(Expression::Unpivot(Box::new(Unpivot {
5838            this: source,
5839            value_column,
5840            name_column,
5841            columns,
5842            alias: None,
5843            value_column_parenthesized,
5844            include_nulls,
5845            extra_value_columns,
5846        })))
5847    }
5848
5849    /// Parse Redshift UNPIVOT in FROM clause for SUPER object traversal
5850    /// Syntax: UNPIVOT expr [AS val_alias AT attr_alias]
5851    /// Examples:
5852    ///   FROM t, UNPIVOT t.arr[0]
5853    ///   FROM t, UNPIVOT t.arr AS val AT attr
5854    fn parse_redshift_unpivot_table(&mut self) -> Result<Expression> {
5855        // Parse the expression (column reference with possible array subscript)
5856        // We need to parse a primary expression that can include:
5857        // - Simple column: c.c_orders
5858        // - Array subscript: c.c_orders[0]
5859        // - Multiple subscripts: c.c_orders[0].items[1]
5860        // Using parse_primary which handles column refs with subscripts
5861        let this = self.parse_primary()?;
5862
5863        // Check for optional AS val_alias AT attr_alias
5864        let alias = if self.match_token(TokenType::As) {
5865            let val_alias = self.expect_identifier_or_keyword()?;
5866            // Check for AT attr_alias
5867            if self.match_text_seq(&["AT"]) {
5868                let attr_alias = self.expect_identifier_or_keyword()?;
5869                // Create alias expression that captures both aliases
5870                // We'll use the val_alias as the main alias and store attr_alias in a way
5871                // the generator can reconstruct "AS val AT attr"
5872                Some(Identifier::new(format!("{} AT {}", val_alias, attr_alias)))
5873            } else {
5874                Some(Identifier::new(val_alias))
5875            }
5876        } else {
5877            None
5878        };
5879
5880        // Return a Pivot expression with unpivot=true
5881        // Use the simplified form pattern where:
5882        // - this: the expression being unpivoted
5883        // - expressions: empty (no ON expressions)
5884        // - unpivot: true
5885        // - alias: captured above
5886        Ok(Expression::Pivot(Box::new(Pivot {
5887            this,
5888            expressions: Vec::new(),
5889            fields: Vec::new(),
5890            using: Vec::new(),
5891            group: None,
5892            unpivot: true,
5893            into: None,
5894            alias,
5895            include_nulls: None,
5896            default_on_null: None,
5897            with: None,
5898        })))
5899    }
5900
5901    /// BigQuery: Parse a table part that may contain hyphens (e.g., project-id)
5902    /// Also handles numeric table parts (e.g., foo.bar.25 -> foo.bar.`25`)
5903    /// Returns the identifier, possibly with merged hyphenated parts and quoted flag set.
5904    fn parse_bigquery_table_part(&mut self) -> Result<Identifier> {
5905        use crate::dialects::DialectType;
5906
5907        // Try to parse a number for BigQuery numeric table parts (e.g., foo.bar.25)
5908        if matches!(self.config.dialect, Some(DialectType::BigQuery))
5909            && self.check(TokenType::Number)
5910        {
5911            let num_token = self.advance().clone();
5912            let mut name = num_token.text.clone();
5913
5914            // Check if followed by more connected tokens (e.g., 25x, 25_, 25ab)
5915            // Numbers followed immediately by identifiers without whitespace are merged
5916            while !self.is_at_end() && self.is_connected() {
5917                let tok = self.advance().clone();
5918                name.push_str(&tok.text);
5919            }
5920
5921            return Ok(Identifier {
5922                name,
5923                quoted: true,
5924                trailing_comments: Vec::new(),
5925                span: None,
5926            });
5927        }
5928
5929        // MySQL numeric-starting identifiers (e.g., 00f, 1d)
5930        if matches!(self.config.dialect, Some(DialectType::MySQL)) && self.check(TokenType::Number)
5931        {
5932            let num_token = self.advance().clone();
5933            let mut name = num_token.text.clone();
5934
5935            // Merge with connected identifier/var tokens only (not punctuation)
5936            while !self.is_at_end()
5937                && self.is_connected()
5938                && (self.check(TokenType::Var) || self.check(TokenType::Identifier))
5939            {
5940                let tok = self.advance().clone();
5941                name.push_str(&tok.text);
5942            }
5943
5944            return Ok(Identifier {
5945                name,
5946                quoted: true,
5947                trailing_comments: Vec::new(),
5948                span: None,
5949            });
5950        }
5951
5952        let mut ident = self.expect_identifier_or_keyword_with_quoted()?;
5953
5954        // BigQuery: merge hyphenated parts (e.g., pro-ject_id -> `pro-ject_id`)
5955        if matches!(self.config.dialect, Some(DialectType::BigQuery)) && !ident.quoted {
5956            // Check if next token is a dash and it looks connected (no space)
5957            if self.check(TokenType::Dash) && self.is_connected_dash() {
5958                let mut name = ident.name.clone();
5959
5960                while self.check(TokenType::Dash) && self.is_connected_dash() {
5961                    self.skip(); // consume dash
5962                    name.push('-');
5963                    // Consume the next part
5964                    let part = self.advance().clone();
5965                    name.push_str(&part.text);
5966                    // Continue consuming connected tokens (for things like a-b-c)
5967                    while !self.is_at_end()
5968                        && self.is_connected()
5969                        && !self.check(TokenType::Dot)
5970                        && !self.check(TokenType::Dash)
5971                        && !self.check(TokenType::LParen)
5972                        && !self.check(TokenType::RParen)
5973                    {
5974                        let tok = self.advance().clone();
5975                        name.push_str(&tok.text);
5976                    }
5977                }
5978
5979                ident = Identifier {
5980                    name,
5981                    quoted: false,
5982                    trailing_comments: Vec::new(),
5983                    span: None,
5984                };
5985            }
5986        }
5987
5988        Ok(ident)
5989    }
5990
5991    /// Check if the current dash token is "connected" to the next token
5992    /// (i.e., the dash and next token are part of a hyphenated identifier)
5993    fn is_connected_dash(&self) -> bool {
5994        if !self.check(TokenType::Dash) {
5995            return false;
5996        }
5997        if self.current + 1 >= self.tokens.len() {
5998            return false;
5999        }
6000        let dash_token = &self.tokens[self.current];
6001        let next_token = &self.tokens[self.current + 1];
6002
6003        // The next token after dash must be an identifier, number, or keyword
6004        // and it must be adjacent (no whitespace between dash and next token)
6005        let next_is_valid = matches!(
6006            next_token.token_type,
6007            TokenType::Identifier
6008                | TokenType::Var
6009                | TokenType::Number
6010                | TokenType::All
6011                | TokenType::Select
6012                | TokenType::From
6013                | TokenType::Where
6014        ) || next_token.token_type.is_keyword();
6015
6016        // Check adjacency: dash ends at dash.end, next starts at next.start
6017        let adjacent = dash_token.span.end + 1 == next_token.span.start
6018            || dash_token.span.end == next_token.span.start;
6019
6020        next_is_valid && adjacent
6021    }
6022
6023    /// Check if the current token is "connected" to the previous token (no whitespace)
6024    fn is_connected(&self) -> bool {
6025        if self.current == 0 || self.current >= self.tokens.len() {
6026            return false;
6027        }
6028        let prev_token = &self.tokens[self.current - 1];
6029        let curr_token = &self.tokens[self.current];
6030        // Tokens are connected if they are immediately adjacent (no characters between them)
6031        // span.end is exclusive, so if prev.end == curr.start, they are adjacent
6032        prev_token.span.end == curr_token.span.start
6033    }
6034
6035    /// Parse a table reference (schema.table format)
6036    fn parse_table_ref(&mut self) -> Result<TableRef> {
6037        // Capture leading comments on the first token (e.g., FROM \n/* comment */\n db.schema.tbl)
6038        let table_ref_leading_comments = self.current_leading_comments().to_vec();
6039        let mut result = self.parse_table_ref_inner()?;
6040        if !table_ref_leading_comments.is_empty() && result.leading_comments.is_empty() {
6041            result.leading_comments = table_ref_leading_comments;
6042        }
6043        Ok(result)
6044    }
6045
6046    fn parse_table_ref_inner(&mut self) -> Result<TableRef> {
6047        // Check for Snowflake IDENTIFIER() function: IDENTIFIER('string') or IDENTIFIER($var)
6048        if self.check_identifier("IDENTIFIER") && self.check_next(TokenType::LParen) {
6049            self.skip(); // consume IDENTIFIER
6050            self.skip(); // consume (
6051                         // Parse the argument: either a string literal, a variable ($foo), or identifier
6052            let arg = if self.check(TokenType::String) {
6053                let s = self.advance().text.clone();
6054                Expression::Literal(Box::new(Literal::String(s)))
6055            } else if self.check(TokenType::Parameter) {
6056                // ?-style parameter
6057                let var = self.advance().text.clone();
6058                Expression::Var(Box::new(crate::expressions::Var { this: var }))
6059            } else if self.check(TokenType::Dollar) {
6060                // $foo style variable - Dollar followed by identifier
6061                self.skip(); // consume $
6062                let var_name = self.expect_identifier()?;
6063                Expression::Var(Box::new(crate::expressions::Var {
6064                    this: format!("${}", var_name),
6065                }))
6066            } else {
6067                // Could be an identifier too
6068                let ident = self.expect_identifier()?;
6069                Expression::Identifier(Identifier::new(ident))
6070            };
6071            self.expect(TokenType::RParen)?;
6072            let trailing_comments = self.previous_trailing_comments().to_vec();
6073            // Create a Function expression to represent IDENTIFIER(arg)
6074            let identifier_func = Expression::Function(Box::new(crate::expressions::Function {
6075                name: "IDENTIFIER".to_string(),
6076                args: vec![arg],
6077                distinct: false,
6078                trailing_comments: Vec::new(),
6079                use_bracket_syntax: false,
6080                no_parens: false,
6081                quoted: false,
6082                span: None,
6083                inferred_type: None,
6084            }));
6085            return Ok(TableRef {
6086                catalog: None,
6087                schema: None,
6088                name: Identifier::empty(),
6089                alias: None,
6090                alias_explicit_as: false,
6091                column_aliases: Vec::new(),
6092                leading_comments: Vec::new(),
6093                trailing_comments,
6094                when: None,
6095                only: false,
6096                final_: false,
6097                table_sample: None,
6098                hints: Vec::new(),
6099                system_time: None,
6100                partitions: Vec::new(),
6101                identifier_func: Some(Box::new(identifier_func)),
6102                changes: None,
6103                version: None,
6104                span: None,
6105            });
6106        }
6107
6108        let first = self.parse_bigquery_table_part()?;
6109
6110        // Check for schema.table format
6111        if self.match_token(TokenType::Dot) {
6112            // Handle TSQL a..b syntax (database..table with empty schema)
6113            if self.check(TokenType::Dot) {
6114                // Two consecutive dots: a..b means catalog..table (empty schema)
6115                self.skip(); // consume second dot
6116                let table = self.parse_bigquery_table_part()?;
6117                let trailing_comments = self.previous_trailing_comments().to_vec();
6118                Ok(TableRef {
6119                    catalog: Some(first),
6120                    schema: Some(Identifier::new("")), // Empty schema represents ..
6121                    name: table,
6122                    alias: None,
6123                    alias_explicit_as: false,
6124                    column_aliases: Vec::new(),
6125                    leading_comments: Vec::new(),
6126                    trailing_comments,
6127                    when: None,
6128                    only: false,
6129                    final_: false,
6130                    table_sample: None,
6131                    hints: Vec::new(),
6132                    system_time: None,
6133                    partitions: Vec::new(),
6134                    identifier_func: None,
6135                    changes: None,
6136                    version: None,
6137                    span: None,
6138                })
6139            } else {
6140                // BigQuery: handle x.* wildcard table reference (e.g., SELECT * FROM x.*)
6141                // After the first dot, if we see a Star token, it's a wildcard table name
6142                if matches!(
6143                    self.config.dialect,
6144                    Some(crate::dialects::DialectType::BigQuery)
6145                ) && self.check(TokenType::Star)
6146                {
6147                    self.skip(); // consume *
6148                    let trailing_comments = self.previous_trailing_comments().to_vec();
6149                    return Ok(TableRef {
6150                        catalog: None,
6151                        schema: Some(first),
6152                        name: Identifier::new("*"),
6153                        alias: None,
6154                        alias_explicit_as: false,
6155                        column_aliases: Vec::new(),
6156                        leading_comments: Vec::new(),
6157                        trailing_comments,
6158                        when: None,
6159                        only: false,
6160                        final_: false,
6161                        table_sample: None,
6162                        hints: Vec::new(),
6163                        system_time: None,
6164                        partitions: Vec::new(),
6165                        identifier_func: None,
6166                        changes: None,
6167                        version: None,
6168                        span: None,
6169                    });
6170                }
6171                let table = self.parse_bigquery_table_part()?;
6172                // Check for catalog.schema.table format
6173                if self.match_token(TokenType::Dot) {
6174                    // BigQuery: handle a.b.* wildcard table reference
6175                    if matches!(
6176                        self.config.dialect,
6177                        Some(crate::dialects::DialectType::BigQuery)
6178                    ) && self.check(TokenType::Star)
6179                    {
6180                        self.skip(); // consume *
6181                        let trailing_comments = self.previous_trailing_comments().to_vec();
6182                        return Ok(TableRef {
6183                            catalog: Some(first),
6184                            schema: Some(table),
6185                            name: Identifier::new("*"),
6186                            alias: None,
6187                            alias_explicit_as: false,
6188                            column_aliases: Vec::new(),
6189                            leading_comments: Vec::new(),
6190                            trailing_comments,
6191                            when: None,
6192                            only: false,
6193                            final_: false,
6194                            table_sample: None,
6195                            hints: Vec::new(),
6196                            system_time: None,
6197                            partitions: Vec::new(),
6198                            identifier_func: None,
6199                            changes: None,
6200                            version: None,
6201                            span: None,
6202                        });
6203                    }
6204                    let actual_table = self.parse_bigquery_table_part()?;
6205                    let trailing_comments = self.previous_trailing_comments().to_vec();
6206                    Ok(TableRef {
6207                        catalog: Some(first),
6208                        schema: Some(table),
6209                        name: actual_table,
6210                        alias: None,
6211                        alias_explicit_as: false,
6212                        column_aliases: Vec::new(),
6213                        leading_comments: Vec::new(),
6214                        trailing_comments,
6215                        when: None,
6216                        only: false,
6217                        final_: false,
6218                        table_sample: None,
6219                        hints: Vec::new(),
6220                        system_time: None,
6221                        partitions: Vec::new(),
6222                        identifier_func: None,
6223                        changes: None,
6224                        version: None,
6225                        span: None,
6226                    })
6227                } else {
6228                    let trailing_comments = self.previous_trailing_comments().to_vec();
6229                    Ok(TableRef {
6230                        catalog: None,
6231                        schema: Some(first),
6232                        name: table,
6233                        alias: None,
6234                        alias_explicit_as: false,
6235                        column_aliases: Vec::new(),
6236                        leading_comments: Vec::new(),
6237                        trailing_comments,
6238                        when: None,
6239                        only: false,
6240                        final_: false,
6241                        table_sample: None,
6242                        hints: Vec::new(),
6243                        system_time: None,
6244                        partitions: Vec::new(),
6245                        identifier_func: None,
6246                        changes: None,
6247                        version: None,
6248                        span: None,
6249                    })
6250                }
6251            }
6252        } else {
6253            let trailing_comments = self.previous_trailing_comments().to_vec();
6254            Ok(TableRef {
6255                catalog: None,
6256                schema: None,
6257                name: first,
6258                alias: None,
6259                alias_explicit_as: false,
6260                column_aliases: Vec::new(),
6261                leading_comments: Vec::new(),
6262                trailing_comments,
6263                when: None,
6264                only: false,
6265                final_: false,
6266                table_sample: None,
6267                hints: Vec::new(),
6268                system_time: None,
6269                partitions: Vec::new(),
6270                identifier_func: None,
6271                changes: None,
6272                version: None,
6273                span: None,
6274            })
6275        }
6276    }
6277
6278    /// Parse a datetime field for EXTRACT function (YEAR, MONTH, DAY, etc.)
6279    fn parse_datetime_field(&mut self) -> Result<DateTimeField> {
6280        let token = self.advance();
6281        let original_name = token.text.clone();
6282        let name = original_name.to_ascii_uppercase();
6283        match name.as_str() {
6284            "YEAR" => Ok(DateTimeField::Year),
6285            "MONTH" => Ok(DateTimeField::Month),
6286            "DAY" => Ok(DateTimeField::Day),
6287            "HOUR" => Ok(DateTimeField::Hour),
6288            "MINUTE" => Ok(DateTimeField::Minute),
6289            "SECOND" => Ok(DateTimeField::Second),
6290            "MILLISECOND" => Ok(DateTimeField::Millisecond),
6291            "MICROSECOND" => Ok(DateTimeField::Microsecond),
6292            "DOW" | "DAYOFWEEK" => Ok(DateTimeField::DayOfWeek),
6293            "DOY" | "DAYOFYEAR" => Ok(DateTimeField::DayOfYear),
6294            "WEEK" => {
6295                // Check for modifier like WEEK(monday)
6296                if self.match_token(TokenType::LParen) {
6297                    let modifier = self.expect_identifier_or_keyword()?;
6298                    self.expect(TokenType::RParen)?;
6299                    Ok(DateTimeField::WeekWithModifier(modifier))
6300                } else {
6301                    Ok(DateTimeField::Week)
6302                }
6303            }
6304            "QUARTER" => Ok(DateTimeField::Quarter),
6305            "EPOCH" => Ok(DateTimeField::Epoch),
6306            "TIMEZONE" => Ok(DateTimeField::Timezone),
6307            "TIMEZONE_HOUR" => Ok(DateTimeField::TimezoneHour),
6308            "TIMEZONE_MINUTE" => Ok(DateTimeField::TimezoneMinute),
6309            "DATE" => Ok(DateTimeField::Date),
6310            "TIME" => Ok(DateTimeField::Time),
6311            // Allow arbitrary field names for dialect-specific functionality
6312            _ => Ok(DateTimeField::Custom(original_name)),
6313        }
6314    }
6315
6316    /// Parse a table expression followed by any joins
6317    /// Used for parenthesized join expressions like (tbl1 CROSS JOIN tbl2)
6318    fn parse_table_expression_with_joins(&mut self) -> Result<(Expression, Vec<Join>)> {
6319        // First parse the left table expression
6320        let left = self.parse_table_expression()?;
6321
6322        // Then parse any joins
6323        let joins = self.parse_joins()?;
6324
6325        Ok((left, joins))
6326    }
6327
6328    /// Parse JOIN clauses
6329    ///
6330    /// Supports right-associative chained JOINs where ON/USING clauses are assigned right-to-left:
6331    /// - `a JOIN b JOIN c ON cond1 ON cond2` means `a JOIN (b JOIN c ON cond1) ON cond2`
6332    /// - The rightmost ON applies to the rightmost unconditioned JOIN
6333    fn parse_joins(&mut self) -> Result<Vec<Join>> {
6334        let mut joins = Vec::with_capacity(2);
6335        let mut nesting_group: usize = 0;
6336
6337        // Loop: Phase 1 (parse JOINs) + Phase 2 (assign deferred conditions)
6338        // After phase 2, if there are more JOIN keywords, continue with another round
6339        loop {
6340            let joins_before = joins.len();
6341
6342            // Phase 1: Parse all JOINs with optional inline ON/USING conditions
6343            loop {
6344                let pos_before_join_kind = self.current;
6345                let join_kind_result = self.try_parse_join_kind();
6346                let (kind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint) =
6347                    match join_kind_result {
6348                        Some(r) => r,
6349                        None => break,
6350                    };
6351                // Collect comments from all tokens consumed by try_parse_join_kind:
6352                // - Leading comments on the first token (comments on a separate line before the join)
6353                // - Trailing comments between join keywords (e.g., INNER /* comment */ JOIN)
6354                let mut join_comments = Vec::new();
6355                // Capture leading comments from the first token of the join kind
6356                if pos_before_join_kind < self.tokens.len() {
6357                    join_comments
6358                        .extend(self.tokens[pos_before_join_kind].comments.iter().cloned());
6359                }
6360                for i in pos_before_join_kind..self.current {
6361                    if i < self.tokens.len() {
6362                        join_comments.extend(self.tokens[i].trailing_comments.iter().cloned());
6363                    }
6364                }
6365                // Snowflake: DIRECTED keyword before JOIN (e.g., CROSS DIRECTED JOIN)
6366                let directed = if needs_join_keyword && self.check_identifier("DIRECTED") {
6367                    self.skip();
6368                    true
6369                } else {
6370                    false
6371                };
6372                if needs_join_keyword {
6373                    self.expect(TokenType::Join)?;
6374                }
6375
6376                // ClickHouse: ARRAY JOIN uses expressions, not table references
6377                let table = if matches!(kind, JoinKind::Array | JoinKind::LeftArray) {
6378                    let mut items = Vec::new();
6379                    // Handle ARRAY JOIN with no arguments (intentional error test)
6380                    if !self.is_at_end()
6381                        && !self.check(TokenType::Semicolon)
6382                        && !self.check(TokenType::RParen)
6383                    {
6384                        loop {
6385                            let expr = self.parse_expression()?;
6386                            let item = if self.match_token(TokenType::As) {
6387                                let alias_name = self.expect_identifier_or_safe_keyword()?;
6388                                Expression::Alias(Box::new(Alias {
6389                                    this: expr,
6390                                    alias: Identifier::new(alias_name),
6391                                    column_aliases: Vec::new(),
6392                                    pre_alias_comments: Vec::new(),
6393                                    trailing_comments: Vec::new(),
6394                                    inferred_type: None,
6395                                }))
6396                            } else {
6397                                expr
6398                            };
6399                            items.push(item);
6400                            if !self.match_token(TokenType::Comma) {
6401                                break;
6402                            }
6403                        }
6404                    } // end if !is_at_end check
6405                    if items.len() == 1 {
6406                        items.pop().unwrap()
6407                    } else if items.is_empty() {
6408                        Expression::Null(Null)
6409                    } else {
6410                        Expression::Tuple(Box::new(Tuple { expressions: items }))
6411                    }
6412                } else {
6413                    self.parse_table_expression()?
6414                };
6415
6416                // Snowflake ASOF JOIN: OFFSET/LIMIT before MATCH_CONDITION are table aliases
6417                let table = if matches!(
6418                    kind,
6419                    JoinKind::AsOf | JoinKind::AsOfLeft | JoinKind::AsOfRight
6420                ) && (self.check(TokenType::Offset) || self.check(TokenType::Limit))
6421                    && self
6422                        .peek_nth(1)
6423                        .map(|t| t.text.eq_ignore_ascii_case("MATCH_CONDITION"))
6424                        == Some(true)
6425                {
6426                    let alias_name = self.advance().text.clone();
6427                    Expression::Alias(Box::new(Alias {
6428                        this: table,
6429                        alias: Identifier::new(alias_name),
6430                        column_aliases: Vec::new(),
6431                        pre_alias_comments: Vec::new(),
6432                        trailing_comments: Vec::new(),
6433                        inferred_type: None,
6434                    }))
6435                } else {
6436                    table
6437                };
6438
6439                // Try to parse inline MATCH_CONDITION/ON/USING (only if not followed by another JOIN)
6440                // We need to peek ahead to see if there's another JOIN keyword coming
6441                let has_match_condition = self.check_identifier("MATCH_CONDITION");
6442                let has_inline_condition = self.check(TokenType::On)
6443                    || self.check(TokenType::Using)
6444                    || has_match_condition;
6445                let next_is_join = self.check_join_keyword();
6446
6447                // Parse MATCH_CONDITION first (Snowflake ASOF JOIN can have MATCH_CONDITION before ON)
6448                let match_condition = if has_match_condition && !next_is_join {
6449                    if self.match_identifier("MATCH_CONDITION") {
6450                        self.expect(TokenType::LParen)?;
6451                        let condition = self.parse_expression()?;
6452                        self.expect(TokenType::RParen)?;
6453                        Some(condition)
6454                    } else {
6455                        None
6456                    }
6457                } else {
6458                    None
6459                };
6460
6461                let (on, using) = if (has_inline_condition || match_condition.is_some())
6462                    && !self.check_join_keyword()
6463                {
6464                    // Parse inline condition only if there's no more JOINs following
6465                    if self.match_token(TokenType::On) {
6466                        (Some(self.parse_expression()?), Vec::new())
6467                    } else if self.match_token(TokenType::Using) {
6468                        // ClickHouse allows USING without parentheses
6469                        let has_parens = self.match_token(TokenType::LParen);
6470                        // Use parse_using_column_list to handle qualified names like t1.col
6471                        let cols = self.parse_using_column_list()?;
6472                        if has_parens {
6473                            self.expect(TokenType::RParen)?;
6474                        }
6475                        (None, cols)
6476                    } else {
6477                        (None, Vec::new())
6478                    }
6479                } else {
6480                    (None, Vec::new())
6481                };
6482
6483                joins.push(Join {
6484                    this: table,
6485                    on,
6486                    using,
6487                    kind,
6488                    use_inner_keyword,
6489                    use_outer_keyword,
6490                    deferred_condition: false,
6491                    join_hint,
6492                    match_condition,
6493                    pivots: Vec::new(),
6494                    comments: join_comments,
6495                    nesting_group,
6496                    directed,
6497                });
6498            }
6499
6500            // Phase 2: Assign deferred ON/USING conditions to unconditioned joins (right-to-left)
6501            // Only consider joins from the current batch (joins_before..)
6502            let unconditioned: Vec<usize> = joins[joins_before..]
6503                .iter()
6504                .enumerate()
6505                .filter(|(_, j)| j.on.is_none() && j.using.is_empty())
6506                .map(|(i, _)| joins_before + i)
6507                .collect();
6508
6509            let mut idx = unconditioned.len();
6510            while idx > 0 {
6511                if self.match_token(TokenType::On) {
6512                    idx -= 1;
6513                    let join_idx = unconditioned[idx];
6514                    joins[join_idx].on = Some(self.parse_expression()?);
6515                    joins[join_idx].deferred_condition = true;
6516                } else if self.match_token(TokenType::Using) {
6517                    idx -= 1;
6518                    let join_idx = unconditioned[idx];
6519                    let has_parens = self.match_token(TokenType::LParen);
6520                    // Handle empty USING ()
6521                    let cols = if has_parens && self.check(TokenType::RParen) {
6522                        Vec::new()
6523                    } else {
6524                        // Use parse_using_column_list to handle qualified names like t1.col
6525                        self.parse_using_column_list()?
6526                    };
6527                    joins[join_idx].using = cols;
6528                    if has_parens {
6529                        self.expect(TokenType::RParen)?;
6530                    }
6531                    joins[join_idx].deferred_condition = true;
6532                } else {
6533                    break;
6534                }
6535            }
6536
6537            // If no new joins were parsed in this round, we're done
6538            if joins.len() == joins_before {
6539                break;
6540            }
6541
6542            // If there are more JOIN keywords after deferred conditions, continue with another round
6543            if !self.check_join_keyword() {
6544                break;
6545            }
6546            nesting_group += 1;
6547        }
6548
6549        Ok(joins)
6550    }
6551
6552    /// Check if the current token starts a JOIN clause
6553    fn check_join_keyword(&self) -> bool {
6554        self.check(TokenType::Join) ||
6555        self.check(TokenType::Inner) ||
6556        self.check(TokenType::Left) ||
6557        self.check(TokenType::Right) ||
6558        self.check(TokenType::Full) ||
6559        self.check(TokenType::Cross) ||
6560        self.check(TokenType::Natural) ||
6561        self.check(TokenType::Outer) ||
6562        // ClickHouse: ARRAY JOIN, GLOBAL JOIN, ALL JOIN, ANY JOIN, PASTE JOIN
6563        (matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) &&
6564            (self.check_identifier("ARRAY") || self.check_identifier("GLOBAL") || self.check(TokenType::All) || self.check(TokenType::Any) || self.check_identifier("PASTE")))
6565    }
6566
6567    /// Try to parse a JOIN kind
6568    /// Returns (JoinKind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint)
6569    fn try_parse_join_kind(&mut self) -> Option<(JoinKind, bool, bool, bool, Option<String>)> {
6570        if matches!(
6571            self.config.dialect,
6572            Some(crate::dialects::DialectType::ClickHouse)
6573        ) {
6574            let start = self.current;
6575            let mut global = false;
6576            let mut strictness: Option<String> = None;
6577            let mut kind: Option<JoinKind> = None;
6578            let mut use_outer = false;
6579            let mut use_inner = false;
6580
6581            if self.match_identifier("GLOBAL") {
6582                global = true;
6583            }
6584
6585            loop {
6586                if strictness.is_none() && self.match_token(TokenType::All) {
6587                    strictness = Some("ALL".to_string());
6588                    continue;
6589                }
6590                if strictness.is_none() && self.match_token(TokenType::Any) {
6591                    strictness = Some("ANY".to_string());
6592                    continue;
6593                }
6594                if strictness.is_none() && self.match_token(TokenType::AsOf) {
6595                    strictness = Some("ASOF".to_string());
6596                    continue;
6597                }
6598                if strictness.is_none() && self.match_token(TokenType::Semi) {
6599                    strictness = Some("SEMI".to_string());
6600                    continue;
6601                }
6602                if strictness.is_none() && self.match_token(TokenType::Anti) {
6603                    strictness = Some("ANTI".to_string());
6604                    continue;
6605                }
6606                if kind.is_none() && self.match_token(TokenType::Left) {
6607                    use_outer = self.match_token(TokenType::Outer);
6608                    use_inner = self.match_token(TokenType::Inner);
6609                    kind = Some(JoinKind::Left);
6610                    continue;
6611                }
6612                if kind.is_none() && self.match_token(TokenType::Right) {
6613                    use_outer = self.match_token(TokenType::Outer);
6614                    use_inner = self.match_token(TokenType::Inner);
6615                    kind = Some(JoinKind::Right);
6616                    continue;
6617                }
6618                if kind.is_none() && self.match_token(TokenType::Full) {
6619                    use_outer = self.match_token(TokenType::Outer);
6620                    kind = Some(JoinKind::Full);
6621                    continue;
6622                }
6623                if kind.is_none() && self.match_token(TokenType::Inner) {
6624                    use_inner = true;
6625                    kind = Some(JoinKind::Inner);
6626                    continue;
6627                }
6628                break;
6629            }
6630
6631            // ClickHouse: ARRAY JOIN or LEFT ARRAY JOIN
6632            if self.check_identifier("ARRAY") && self.check_next(TokenType::Join) {
6633                let array_kind = if matches!(kind, Some(JoinKind::Left)) {
6634                    JoinKind::LeftArray
6635                } else {
6636                    JoinKind::Array
6637                };
6638                self.skip(); // consume ARRAY
6639                             // JOIN will be consumed by caller
6640                return Some((array_kind, true, false, false, None));
6641            }
6642
6643            // ClickHouse: PASTE JOIN (positional join, no ON/USING)
6644            if self.check_identifier("PASTE") && self.check_next(TokenType::Join) {
6645                self.skip(); // consume PASTE
6646                             // JOIN will be consumed by caller
6647                return Some((JoinKind::Paste, true, false, false, None));
6648            }
6649
6650            if global || strictness.is_some() || kind.is_some() {
6651                if self.check(TokenType::Join) {
6652                    let join_kind = kind.unwrap_or(JoinKind::Inner);
6653                    let mut hints = Vec::new();
6654                    if global {
6655                        hints.push("GLOBAL".to_string());
6656                    }
6657                    if let Some(strict) = strictness {
6658                        hints.push(strict);
6659                    }
6660                    let join_hint = if hints.is_empty() {
6661                        None
6662                    } else {
6663                        Some(hints.join(" "))
6664                    };
6665                    return Some((join_kind, true, use_inner, use_outer, join_hint));
6666                } else {
6667                    self.current = start;
6668                }
6669            }
6670        }
6671
6672        // Check for ASOF first (DuckDB/Snowflake) - can be followed by LEFT/RIGHT/etc.
6673        if self.match_token(TokenType::AsOf) {
6674            // ASOF can be followed by LEFT, RIGHT, INNER, or standalone
6675            if self.match_token(TokenType::Left) {
6676                let use_outer = self.match_token(TokenType::Outer);
6677                Some((JoinKind::AsOfLeft, true, false, use_outer, None))
6678            } else if self.match_token(TokenType::Right) {
6679                let use_outer = self.match_token(TokenType::Outer);
6680                Some((JoinKind::AsOfRight, true, false, use_outer, None))
6681            } else if self.match_token(TokenType::Inner) {
6682                Some((JoinKind::AsOf, true, true, false, None))
6683            } else {
6684                // Standalone ASOF JOIN
6685                Some((JoinKind::AsOf, true, false, false, None))
6686            }
6687        } else if self.check(TokenType::Inner) {
6688            // Check if INNER is followed by a set operation (BigQuery INNER UNION/INTERSECT/EXCEPT)
6689            // In that case, don't treat it as a JOIN keyword
6690            let saved = self.current;
6691            self.skip(); // consume INNER
6692            if self.check(TokenType::Union)
6693                || self.check(TokenType::Intersect)
6694                || self.check(TokenType::Except)
6695            {
6696                self.current = saved; // backtrack
6697                return None;
6698            }
6699            // Check for TSQL join hints: INNER LOOP JOIN, INNER HASH JOIN, INNER MERGE JOIN
6700            let join_hint = self.parse_tsql_join_hint();
6701            Some((JoinKind::Inner, true, true, false, join_hint)) // INNER keyword was explicit
6702        } else if self.check(TokenType::Left) {
6703            // Check if LEFT is followed by a set operation (BigQuery LEFT UNION/INTERSECT/EXCEPT)
6704            let saved = self.current;
6705            self.skip(); // consume LEFT
6706                         // LEFT can be followed by OUTER/INNER then set op, or directly by set op
6707            let at_set_op = self.check(TokenType::Union)
6708                || self.check(TokenType::Intersect)
6709                || self.check(TokenType::Except);
6710            let at_inner_set_op = self.check(TokenType::Inner) && {
6711                let saved2 = self.current;
6712                self.skip();
6713                let is_setop = self.check(TokenType::Union)
6714                    || self.check(TokenType::Intersect)
6715                    || self.check(TokenType::Except);
6716                self.current = saved2;
6717                is_setop
6718            };
6719            if at_set_op || at_inner_set_op {
6720                self.current = saved; // backtrack
6721                return None;
6722            }
6723            // Continue with normal LEFT JOIN parsing
6724            self.current = saved;
6725            self.match_token(TokenType::Left); // re-consume LEFT
6726            let use_outer = self.match_token(TokenType::Outer);
6727            let use_inner = self.match_token(TokenType::Inner);
6728            let join_hint = self.parse_tsql_join_hint();
6729            // Check for SEMI, ANTI, or LATERAL
6730            if self.match_token(TokenType::Semi) {
6731                Some((JoinKind::LeftSemi, true, use_inner, use_outer, join_hint))
6732            } else if self.match_token(TokenType::Anti) {
6733                Some((JoinKind::LeftAnti, true, use_inner, use_outer, join_hint))
6734            } else if self.match_token(TokenType::Lateral) {
6735                Some((JoinKind::LeftLateral, true, use_inner, use_outer, join_hint))
6736            } else {
6737                Some((JoinKind::Left, true, use_inner, use_outer, join_hint))
6738            }
6739        } else if self.check(TokenType::Right) {
6740            // Check if RIGHT is followed by a set operation (BigQuery RIGHT UNION/INTERSECT/EXCEPT)
6741            let saved = self.current;
6742            self.skip(); // consume RIGHT
6743            let at_set_op = self.check(TokenType::Union)
6744                || self.check(TokenType::Intersect)
6745                || self.check(TokenType::Except);
6746            let at_inner_set_op = self.check(TokenType::Inner) && {
6747                let saved2 = self.current;
6748                self.skip();
6749                let is_setop = self.check(TokenType::Union)
6750                    || self.check(TokenType::Intersect)
6751                    || self.check(TokenType::Except);
6752                self.current = saved2;
6753                is_setop
6754            };
6755            if at_set_op || at_inner_set_op {
6756                self.current = saved; // backtrack
6757                return None;
6758            }
6759            // Continue with normal RIGHT JOIN parsing
6760            self.current = saved;
6761            self.match_token(TokenType::Right); // re-consume RIGHT
6762            let use_outer = self.match_token(TokenType::Outer);
6763            let use_inner = self.match_token(TokenType::Inner);
6764            let join_hint = self.parse_tsql_join_hint();
6765            // Check for SEMI or ANTI
6766            if self.match_token(TokenType::Semi) {
6767                Some((JoinKind::RightSemi, true, use_inner, use_outer, join_hint))
6768            } else if self.match_token(TokenType::Anti) {
6769                Some((JoinKind::RightAnti, true, use_inner, use_outer, join_hint))
6770            } else {
6771                Some((JoinKind::Right, true, use_inner, use_outer, join_hint))
6772            }
6773        } else if self.check(TokenType::Full) {
6774            // Check if FULL is followed by a set operation (BigQuery FULL UNION/INTERSECT/EXCEPT)
6775            let saved = self.current;
6776            self.skip(); // consume FULL
6777            let at_set_op = self.check(TokenType::Union)
6778                || self.check(TokenType::Intersect)
6779                || self.check(TokenType::Except);
6780            let at_inner_set_op = self.check(TokenType::Inner) && {
6781                let saved2 = self.current;
6782                self.skip();
6783                let is_setop = self.check(TokenType::Union)
6784                    || self.check(TokenType::Intersect)
6785                    || self.check(TokenType::Except);
6786                self.current = saved2;
6787                is_setop
6788            };
6789            if at_set_op || at_inner_set_op {
6790                self.current = saved; // backtrack
6791                return None;
6792            }
6793            // Continue with normal FULL JOIN parsing
6794            self.current = saved;
6795            self.match_token(TokenType::Full); // re-consume FULL
6796            let use_outer = self.match_token(TokenType::Outer);
6797            let join_hint = self.parse_tsql_join_hint();
6798            Some((JoinKind::Full, true, false, use_outer, join_hint))
6799        } else if self.match_token(TokenType::Cross) {
6800            // CROSS JOIN or CROSS APPLY
6801            if self.match_token(TokenType::Apply) {
6802                Some((JoinKind::CrossApply, false, false, false, None))
6803            } else {
6804                Some((JoinKind::Cross, true, false, false, None))
6805            }
6806        } else if self.match_token(TokenType::Natural) {
6807            // NATURAL can be followed by LEFT, RIGHT, INNER, FULL, or just JOIN
6808            if self.match_token(TokenType::Left) {
6809                let use_outer = self.match_token(TokenType::Outer);
6810                Some((JoinKind::NaturalLeft, true, false, use_outer, None))
6811            } else if self.match_token(TokenType::Right) {
6812                let use_outer = self.match_token(TokenType::Outer);
6813                Some((JoinKind::NaturalRight, true, false, use_outer, None))
6814            } else if self.match_token(TokenType::Full) {
6815                let use_outer = self.match_token(TokenType::Outer);
6816                Some((JoinKind::NaturalFull, true, false, use_outer, None))
6817            } else if self.match_token(TokenType::Inner) {
6818                Some((JoinKind::Natural, true, true, false, None))
6819            } else {
6820                Some((JoinKind::Natural, true, false, false, None))
6821            }
6822        } else if self.match_token(TokenType::Outer) {
6823            // OUTER APPLY or standalone OUTER JOIN
6824            if self.match_token(TokenType::Apply) {
6825                Some((JoinKind::OuterApply, false, false, true, None))
6826            } else {
6827                // Standalone OUTER JOIN (without LEFT/RIGHT/FULL)
6828                Some((JoinKind::Outer, true, false, true, None))
6829            }
6830        } else if self.check(TokenType::Lateral) {
6831            // Check if this is LATERAL VIEW (Hive/Spark syntax) vs LATERAL JOIN
6832            if self.current + 1 < self.tokens.len()
6833                && self.tokens[self.current + 1].token_type == TokenType::View
6834            {
6835                // LATERAL VIEW is not a JOIN type, return None
6836                None
6837            } else {
6838                self.skip(); // Consume LATERAL
6839                Some((JoinKind::Lateral, true, false, false, None))
6840            }
6841        } else if self.match_token(TokenType::Semi) {
6842            Some((JoinKind::Semi, true, false, false, None))
6843        } else if self.match_token(TokenType::Anti) {
6844            Some((JoinKind::Anti, true, false, false, None))
6845        } else if self.check_identifier("POSITIONAL") && self.check_next(TokenType::Join) {
6846            // DuckDB POSITIONAL JOIN
6847            self.skip(); // consume POSITIONAL
6848            Some((JoinKind::Positional, true, false, false, None))
6849        } else if self.match_token(TokenType::StraightJoin) {
6850            // STRAIGHT_JOIN in MySQL - doesn't need JOIN keyword after it
6851            Some((JoinKind::Straight, false, false, false, None))
6852        } else if self.check(TokenType::Join) {
6853            Some((JoinKind::Inner, true, false, false, None)) // Default JOIN is INNER (without explicit INNER keyword)
6854        } else if self.match_token(TokenType::Comma) {
6855            // Comma-separated tables: FROM a, b (old-style ANSI join syntax)
6856            Some((JoinKind::Implicit, false, false, false, None)) // No JOIN keyword needed
6857        } else {
6858            None
6859        }
6860    }
6861
6862    /// Parse TSQL join hints: LOOP, HASH, MERGE, REMOTE
6863    fn parse_tsql_join_hint(&mut self) -> Option<String> {
6864        if self.check_identifier("LOOP") {
6865            self.skip();
6866            Some("LOOP".to_string())
6867        } else if self.check_identifier("HASH") {
6868            self.skip();
6869            Some("HASH".to_string())
6870        } else if self.check_identifier("REMOTE") {
6871            self.skip();
6872            Some("REMOTE".to_string())
6873        } else if self.check(TokenType::Merge) && {
6874            // Be careful: MERGE is also a keyword for MERGE statement
6875            // Only treat as hint if followed by JOIN
6876            let next_pos = self.current + 1;
6877            next_pos < self.tokens.len() && self.tokens[next_pos].token_type == TokenType::Join
6878        } {
6879            self.skip();
6880            Some("MERGE".to_string())
6881        } else {
6882            None
6883        }
6884    }
6885
6886    /// Parse GROUP BY clause
6887    fn parse_group_by(&mut self) -> Result<GroupBy> {
6888        // Check for optional ALL/DISTINCT modifier
6889        // Some(true) = ALL, Some(false) = DISTINCT, None = no modifier
6890        let all = if self.match_token(TokenType::All) {
6891            Some(true)
6892        } else if self.match_token(TokenType::Distinct) {
6893            Some(false)
6894        } else {
6895            None
6896        };
6897
6898        let mut expressions = Vec::new();
6899
6900        // GROUP BY ALL / GROUP BY DISTINCT without following CUBE/ROLLUP/expressions
6901        // should return early (e.g., Snowflake's "GROUP BY ALL" without column list).
6902        // But in Presto/Trino, ALL/DISTINCT can be followed by CUBE/ROLLUP expressions.
6903        if all.is_some() && self.is_at_query_modifier_or_end() {
6904            return Ok(GroupBy {
6905                expressions,
6906                all,
6907                totals: false,
6908                comments: Vec::new(),
6909            });
6910        }
6911
6912        // GROUP BY ALL WITH ROLLUP/CUBE/TOTALS — skip expression parsing, go straight to modifiers
6913        if all.is_some()
6914            && self.check(TokenType::With)
6915            && (self.check_next(TokenType::Cube)
6916                || self.check_next(TokenType::Rollup)
6917                || self.check_next_identifier("TOTALS"))
6918        {
6919            let mut totals = false;
6920            // Process WITH ROLLUP/CUBE
6921            if self.check_next(TokenType::Cube) || self.check_next(TokenType::Rollup) {
6922                self.skip(); // consume WITH
6923                if self.match_token(TokenType::Cube) {
6924                    expressions.push(Expression::Cube(Box::new(Cube {
6925                        expressions: Vec::new(),
6926                    })));
6927                } else if self.match_token(TokenType::Rollup) {
6928                    expressions.push(Expression::Rollup(Box::new(Rollup {
6929                        expressions: Vec::new(),
6930                    })));
6931                }
6932            }
6933            // Check for WITH TOTALS (possibly chained after ROLLUP/CUBE)
6934            if self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
6935                self.skip(); // WITH
6936                self.skip(); // TOTALS
6937                totals = true;
6938            }
6939            return Ok(GroupBy {
6940                expressions,
6941                all,
6942                totals,
6943                comments: Vec::new(),
6944            });
6945        }
6946
6947        loop {
6948            // Check for GROUPING SETS, CUBE, ROLLUP
6949            let expr = if self.check_identifier("GROUPING")
6950                && self
6951                    .peek_nth(1)
6952                    .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS"))
6953                && {
6954                    self.skip();
6955                    self.skip();
6956                    true
6957                } {
6958                // GROUPING SETS (...)
6959                self.expect(TokenType::LParen)?;
6960                let args = self.parse_grouping_sets_args()?;
6961                self.expect(TokenType::RParen)?;
6962                Expression::Function(Box::new(Function {
6963                    name: "GROUPING SETS".to_string(),
6964                    args,
6965                    distinct: false,
6966                    trailing_comments: Vec::new(),
6967                    use_bracket_syntax: false,
6968                    no_parens: false,
6969                    quoted: false,
6970                    span: None,
6971                    inferred_type: None,
6972                }))
6973            } else if self.match_token(TokenType::Cube) {
6974                // CUBE (...)
6975                self.expect(TokenType::LParen)?;
6976                let args = self.parse_expression_list()?;
6977                self.expect(TokenType::RParen)?;
6978                Expression::Function(Box::new(Function {
6979                    name: "CUBE".to_string(),
6980                    args,
6981                    distinct: false,
6982                    trailing_comments: Vec::new(),
6983                    use_bracket_syntax: false,
6984                    no_parens: false,
6985                    quoted: false,
6986                    span: None,
6987                    inferred_type: None,
6988                }))
6989            } else if self.match_token(TokenType::Rollup) {
6990                // ROLLUP (...)
6991                self.expect(TokenType::LParen)?;
6992                let args = self.parse_expression_list()?;
6993                self.expect(TokenType::RParen)?;
6994                Expression::Function(Box::new(Function {
6995                    name: "ROLLUP".to_string(),
6996                    args,
6997                    distinct: false,
6998                    trailing_comments: Vec::new(),
6999                    use_bracket_syntax: false,
7000                    no_parens: false,
7001                    quoted: false,
7002                    span: None,
7003                    inferred_type: None,
7004                }))
7005            } else {
7006                self.parse_expression()?
7007            };
7008
7009            // ClickHouse: GROUP BY expr AS alias
7010            let expr = if matches!(
7011                self.config.dialect,
7012                Some(crate::dialects::DialectType::ClickHouse)
7013            ) && self.check(TokenType::As)
7014                && !self.check_next(TokenType::LParen)
7015            {
7016                self.skip(); // consume AS
7017                let alias = self.expect_identifier_or_keyword_with_quoted()?;
7018                Expression::Alias(Box::new(Alias::new(expr, alias)))
7019            } else {
7020                expr
7021            };
7022
7023            expressions.push(expr);
7024
7025            if !self.match_token(TokenType::Comma) {
7026                // Allow adjacent CUBE/ROLLUP/GROUPING SETS without comma separator
7027                // e.g., GROUP BY CUBE(a) ROLLUP(b), GROUPING SETS((c, d))
7028                if self.check(TokenType::Cube)
7029                    || self.check(TokenType::Rollup)
7030                    || (self.check_identifier("GROUPING")
7031                        && self
7032                            .peek_nth(1)
7033                            .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS")))
7034                {
7035                    continue;
7036                }
7037                break;
7038            }
7039        }
7040
7041        // Check for trailing WITH CUBE or WITH ROLLUP (Hive/MySQL syntax)
7042        // This is different from CUBE(...) or ROLLUP(...) which are parsed inline above
7043        // Use lookahead to avoid consuming WITH if it's not followed by CUBE or ROLLUP
7044        // (e.g., Redshift's WITH NO SCHEMA BINDING should not be consumed here)
7045        if self.check(TokenType::With)
7046            && (self.check_next(TokenType::Cube) || self.check_next(TokenType::Rollup))
7047        {
7048            self.skip(); // consume WITH
7049            if self.match_token(TokenType::Cube) {
7050                // WITH CUBE - add Cube with empty expressions
7051                expressions.push(Expression::Cube(Box::new(Cube {
7052                    expressions: Vec::new(),
7053                })));
7054            } else if self.match_token(TokenType::Rollup) {
7055                // WITH ROLLUP - add Rollup with empty expressions
7056                expressions.push(Expression::Rollup(Box::new(Rollup {
7057                    expressions: Vec::new(),
7058                })));
7059            }
7060        }
7061
7062        // ClickHouse: WITH TOTALS
7063        let totals = if self.check(TokenType::With) && self.check_next_identifier("TOTALS") {
7064            self.skip(); // consume WITH
7065            self.skip(); // consume TOTALS
7066            true
7067        } else {
7068            false
7069        };
7070
7071        Ok(GroupBy {
7072            expressions,
7073            all,
7074            totals,
7075            comments: Vec::new(),
7076        })
7077    }
7078
7079    /// Parse GROUPING SETS arguments which can include tuples like (x, y), nested GROUPING SETS, CUBE, ROLLUP
7080    fn parse_grouping_sets_args(&mut self) -> Result<Vec<Expression>> {
7081        let mut args = Vec::new();
7082
7083        loop {
7084            // Check for nested GROUPING SETS, CUBE, ROLLUP
7085            let expr = if self.check_identifier("GROUPING")
7086                && self
7087                    .peek_nth(1)
7088                    .map_or(false, |t| t.text.eq_ignore_ascii_case("SETS"))
7089                && {
7090                    self.skip();
7091                    self.skip();
7092                    true
7093                } {
7094                // Nested GROUPING SETS (...)
7095                self.expect(TokenType::LParen)?;
7096                let inner_args = self.parse_grouping_sets_args()?;
7097                self.expect(TokenType::RParen)?;
7098                Expression::Function(Box::new(Function {
7099                    name: "GROUPING SETS".to_string(),
7100                    args: inner_args,
7101                    distinct: false,
7102                    trailing_comments: Vec::new(),
7103                    use_bracket_syntax: false,
7104                    no_parens: false,
7105                    quoted: false,
7106                    span: None,
7107                    inferred_type: None,
7108                }))
7109            } else if self.match_token(TokenType::Cube) {
7110                // CUBE (...)
7111                self.expect(TokenType::LParen)?;
7112                let inner_args = self.parse_expression_list()?;
7113                self.expect(TokenType::RParen)?;
7114                Expression::Function(Box::new(Function {
7115                    name: "CUBE".to_string(),
7116                    args: inner_args,
7117                    distinct: false,
7118                    trailing_comments: Vec::new(),
7119                    use_bracket_syntax: false,
7120                    no_parens: false,
7121                    quoted: false,
7122                    span: None,
7123                    inferred_type: None,
7124                }))
7125            } else if self.match_token(TokenType::Rollup) {
7126                // ROLLUP (...)
7127                self.expect(TokenType::LParen)?;
7128                let inner_args = self.parse_expression_list()?;
7129                self.expect(TokenType::RParen)?;
7130                Expression::Function(Box::new(Function {
7131                    name: "ROLLUP".to_string(),
7132                    args: inner_args,
7133                    distinct: false,
7134                    trailing_comments: Vec::new(),
7135                    use_bracket_syntax: false,
7136                    no_parens: false,
7137                    quoted: false,
7138                    span: None,
7139                    inferred_type: None,
7140                }))
7141            } else if self.check(TokenType::LParen) {
7142                // This could be a tuple like (x, y) or empty ()
7143                self.skip(); // consume (
7144                if self.check(TokenType::RParen) {
7145                    // Empty tuple ()
7146                    self.skip();
7147                    Expression::Tuple(Box::new(Tuple {
7148                        expressions: Vec::new(),
7149                    }))
7150                } else {
7151                    let inner = self.parse_expression_list()?;
7152                    self.expect(TokenType::RParen)?;
7153                    Expression::Tuple(Box::new(Tuple { expressions: inner }))
7154                }
7155            } else {
7156                self.parse_expression()?
7157            };
7158
7159            args.push(expr);
7160
7161            if !self.match_token(TokenType::Comma) {
7162                break;
7163            }
7164        }
7165
7166        Ok(args)
7167    }
7168
7169    /// Parse ORDER BY clause
7170    fn parse_order_by(&mut self) -> Result<OrderBy> {
7171        self.parse_order_by_with_siblings(false)
7172    }
7173
7174    /// Parse ORDER BY clause with optional siblings flag (Oracle ORDER SIBLINGS BY)
7175    fn parse_order_by_with_siblings(&mut self, siblings: bool) -> Result<OrderBy> {
7176        let mut expressions = Vec::new();
7177
7178        loop {
7179            let expr = self.parse_expression()?;
7180
7181            // ClickHouse: ORDER BY expr AS alias — allow AS alias before DESC/ASC
7182            // But NOT AS SELECT/WITH which would be CREATE TABLE ... AS SELECT
7183            let expr = if matches!(
7184                self.config.dialect,
7185                Some(crate::dialects::DialectType::ClickHouse)
7186            ) && self.check(TokenType::As)
7187                && !self.check_next(TokenType::LParen)
7188                && !self.check_next(TokenType::Select)
7189                && !self.check_next(TokenType::With)
7190            {
7191                self.skip(); // consume AS
7192                let alias = self.expect_identifier_or_keyword_with_quoted()?;
7193                Expression::Alias(Box::new(Alias::new(expr, alias)))
7194            } else {
7195                expr
7196            };
7197
7198            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7199                (true, false)
7200            } else if self.match_token(TokenType::Asc) {
7201                (false, true)
7202            } else {
7203                (false, false)
7204            };
7205
7206            let nulls_first = if self.match_token(TokenType::Nulls) {
7207                if self.match_token(TokenType::First) {
7208                    Some(true)
7209                } else if self.match_token(TokenType::Last) {
7210                    Some(false)
7211                } else {
7212                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7213                }
7214            } else {
7215                None
7216            };
7217
7218            // Parse optional WITH FILL clause (ClickHouse)
7219            let with_fill = if self.match_text_seq(&["WITH", "FILL"]) {
7220                let from_ = if self.match_token(TokenType::From) {
7221                    Some(Box::new(self.parse_or()?))
7222                } else {
7223                    None
7224                };
7225                let to = if self.match_text_seq(&["TO"]) {
7226                    Some(Box::new(self.parse_or()?))
7227                } else {
7228                    None
7229                };
7230                let step = if self.match_text_seq(&["STEP"]) {
7231                    Some(Box::new(self.parse_or()?))
7232                } else {
7233                    None
7234                };
7235                // ClickHouse: STALENESS [INTERVAL] expr
7236                let staleness = if self.match_text_seq(&["STALENESS"]) {
7237                    Some(Box::new(self.parse_or()?))
7238                } else {
7239                    None
7240                };
7241                let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
7242                    if self.match_token(TokenType::LParen) {
7243                        // Parse INTERPOLATE items: identifier [AS expression], ...
7244                        let mut items = Vec::new();
7245                        loop {
7246                            if self.check(TokenType::RParen) {
7247                                break;
7248                            }
7249                            let quoted = self.check(TokenType::QuotedIdentifier);
7250                            let name_text = self.expect_identifier_or_safe_keyword()?;
7251                            let name_id = Identifier {
7252                                name: name_text,
7253                                quoted,
7254                                trailing_comments: Vec::new(),
7255                                span: None,
7256                            };
7257                            let item = if self.match_token(TokenType::As) {
7258                                let expr = self.parse_expression()?;
7259                                // Store as Alias: this=expression, alias=name
7260                                Expression::Alias(Box::new(Alias {
7261                                    this: expr,
7262                                    alias: name_id,
7263                                    column_aliases: Vec::new(),
7264                                    pre_alias_comments: Vec::new(),
7265                                    trailing_comments: Vec::new(),
7266                                    inferred_type: None,
7267                                }))
7268                            } else {
7269                                Expression::Identifier(name_id)
7270                            };
7271                            items.push(item);
7272                            if !self.match_token(TokenType::Comma) {
7273                                break;
7274                            }
7275                        }
7276                        self.expect(TokenType::RParen)?;
7277                        if items.len() == 1 {
7278                            Some(Box::new(items.into_iter().next().unwrap()))
7279                        } else {
7280                            Some(Box::new(Expression::Tuple(Box::new(
7281                                crate::expressions::Tuple { expressions: items },
7282                            ))))
7283                        }
7284                    } else {
7285                        None
7286                    }
7287                } else {
7288                    None
7289                };
7290                Some(Box::new(WithFill {
7291                    from_,
7292                    to,
7293                    step,
7294                    staleness,
7295                    interpolate,
7296                }))
7297            } else {
7298                None
7299            };
7300
7301            expressions.push(Ordered {
7302                this: expr,
7303                desc,
7304                nulls_first,
7305                explicit_asc,
7306                with_fill,
7307            });
7308
7309            if !self.match_token(TokenType::Comma) {
7310                break;
7311            }
7312
7313            // Handle trailing comma: if at end of input or semicolon, break
7314            if self.is_at_end() || self.check(TokenType::Semicolon) {
7315                break;
7316            }
7317        }
7318
7319        Ok(OrderBy {
7320            expressions,
7321            siblings,
7322            comments: Vec::new(),
7323        })
7324    }
7325
7326    /// Parse query modifiers (ORDER BY, LIMIT, OFFSET, DISTRIBUTE BY, SORT BY, CLUSTER BY) for parenthesized queries
7327    /// e.g., (SELECT 1) ORDER BY x LIMIT 1 OFFSET 1
7328    /// e.g., (SELECT 1 UNION SELECT 2) DISTRIBUTE BY z SORT BY x
7329    fn parse_query_modifiers(&mut self, inner: Expression) -> Result<Expression> {
7330        // Parse DISTRIBUTE BY (Hive/Spark)
7331        let distribute_by = if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
7332            let exprs = self.parse_expression_list()?;
7333            Some(DistributeBy { expressions: exprs })
7334        } else {
7335            None
7336        };
7337
7338        // Parse SORT BY (Hive/Spark) or CLUSTER BY (Hive/Spark)
7339        let (sort_by, cluster_by) = if self.match_keywords(&[TokenType::Sort, TokenType::By]) {
7340            // SORT BY
7341            let mut orders = Vec::new();
7342            loop {
7343                if let Some(ordered) = self.parse_ordered_item()? {
7344                    orders.push(ordered);
7345                } else {
7346                    break;
7347                }
7348                if !self.match_token(TokenType::Comma) {
7349                    break;
7350                }
7351            }
7352            (
7353                Some(SortBy {
7354                    expressions: orders,
7355                }),
7356                None,
7357            )
7358        } else if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
7359            // CLUSTER BY
7360            let mut orders = Vec::new();
7361            loop {
7362                if let Some(ordered) = self.parse_ordered_item()? {
7363                    orders.push(ordered);
7364                } else {
7365                    break;
7366                }
7367                if !self.match_token(TokenType::Comma) {
7368                    break;
7369                }
7370            }
7371            (
7372                None,
7373                Some(ClusterBy {
7374                    expressions: orders,
7375                }),
7376            )
7377        } else {
7378            (None, None)
7379        };
7380
7381        // Parse ORDER BY
7382        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
7383            Some(self.parse_order_by()?)
7384        } else {
7385            None
7386        };
7387
7388        // Parse LIMIT
7389        let limit = if self.match_token(TokenType::Limit) {
7390            Some(Limit {
7391                this: self.parse_expression()?,
7392                percent: false,
7393                comments: Vec::new(),
7394            })
7395        } else {
7396            None
7397        };
7398
7399        // Parse OFFSET
7400        let offset = if self.match_token(TokenType::Offset) {
7401            Some(Offset {
7402                this: self.parse_expression()?,
7403                rows: None,
7404            })
7405        } else {
7406            None
7407        };
7408
7409        // If we have any modifiers, wrap in a Subquery with the modifiers
7410        if order_by.is_some()
7411            || limit.is_some()
7412            || offset.is_some()
7413            || distribute_by.is_some()
7414            || sort_by.is_some()
7415            || cluster_by.is_some()
7416        {
7417            // If inner is already a Subquery, add modifiers to it instead of double-wrapping
7418            if let Expression::Subquery(mut subq) = inner {
7419                subq.order_by = order_by;
7420                subq.limit = limit;
7421                subq.offset = offset;
7422                subq.distribute_by = distribute_by;
7423                subq.sort_by = sort_by;
7424                subq.cluster_by = cluster_by;
7425                Ok(Expression::Subquery(subq))
7426            } else if let Expression::Paren(paren) = inner {
7427                // If inner is a Paren containing a Subquery or other query, unwrap it
7428                // and add modifiers to a new Subquery wrapping the Paren
7429                // This handles cases like ((SELECT 1)) LIMIT 1
7430                Ok(Expression::Subquery(Box::new(Subquery {
7431                    this: Expression::Paren(paren),
7432                    alias: None,
7433                    column_aliases: Vec::new(),
7434                    order_by,
7435                    limit,
7436                    offset,
7437                    distribute_by,
7438                    sort_by,
7439                    cluster_by,
7440                    lateral: false,
7441                    modifiers_inside: false,
7442                    trailing_comments: Vec::new(),
7443                    inferred_type: None,
7444                })))
7445            } else {
7446                Ok(Expression::Subquery(Box::new(Subquery {
7447                    this: inner,
7448                    alias: None,
7449                    column_aliases: Vec::new(),
7450                    order_by,
7451                    limit,
7452                    offset,
7453                    distribute_by,
7454                    sort_by,
7455                    cluster_by,
7456                    lateral: false,
7457                    modifiers_inside: false,
7458                    trailing_comments: Vec::new(),
7459                    inferred_type: None,
7460                })))
7461            }
7462        } else {
7463            // No modifiers - return inner as-is (don't double-wrap if already a Subquery)
7464            Ok(inner)
7465        }
7466    }
7467
7468    /// Parse ORDER BY expressions for use inside aggregate functions
7469    /// Returns Vec<Ordered> instead of OrderBy struct
7470    fn parse_order_by_list(&mut self) -> Result<Vec<Ordered>> {
7471        let mut expressions = Vec::new();
7472
7473        loop {
7474            let expr = self.parse_expression()?;
7475
7476            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7477                (true, false)
7478            } else if self.match_token(TokenType::Asc) {
7479                (false, true)
7480            } else {
7481                (false, false)
7482            };
7483
7484            let nulls_first = if self.match_token(TokenType::Nulls) {
7485                if self.match_token(TokenType::First) {
7486                    Some(true)
7487                } else if self.match_token(TokenType::Last) {
7488                    Some(false)
7489                } else {
7490                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7491                }
7492            } else {
7493                None
7494            };
7495
7496            expressions.push(Ordered {
7497                this: expr,
7498                desc,
7499                nulls_first,
7500                explicit_asc,
7501                with_fill: None,
7502            });
7503
7504            if !self.match_token(TokenType::Comma) {
7505                break;
7506            }
7507        }
7508
7509        Ok(expressions)
7510    }
7511
7512    /// Parse DISTRIBUTE BY clause (Hive/Spark)
7513    fn parse_distribute_by(&mut self) -> Result<DistributeBy> {
7514        let mut expressions = Vec::new();
7515
7516        loop {
7517            expressions.push(self.parse_expression()?);
7518            if !self.match_token(TokenType::Comma) {
7519                break;
7520            }
7521        }
7522
7523        Ok(DistributeBy { expressions })
7524    }
7525
7526    /// Parse CLUSTER BY clause (Hive/Spark)
7527    fn parse_cluster_by(&mut self) -> Result<ClusterBy> {
7528        let mut expressions = Vec::new();
7529
7530        loop {
7531            let expr = self.parse_expression()?;
7532
7533            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7534                (true, false)
7535            } else if self.match_token(TokenType::Asc) {
7536                (false, true)
7537            } else {
7538                (false, false)
7539            };
7540
7541            expressions.push(Ordered {
7542                this: expr,
7543                desc,
7544                nulls_first: None,
7545                explicit_asc,
7546                with_fill: None,
7547            });
7548
7549            if !self.match_token(TokenType::Comma) {
7550                break;
7551            }
7552        }
7553
7554        Ok(ClusterBy { expressions })
7555    }
7556
7557    /// Parse SORT BY clause (Hive/Spark)
7558    fn parse_sort_by(&mut self) -> Result<SortBy> {
7559        let mut expressions = Vec::new();
7560
7561        loop {
7562            let expr = self.parse_expression()?;
7563
7564            let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
7565                (true, false)
7566            } else if self.match_token(TokenType::Asc) {
7567                (false, true)
7568            } else {
7569                (false, false)
7570            };
7571
7572            let nulls_first = if self.match_token(TokenType::Nulls) {
7573                if self.match_token(TokenType::First) {
7574                    Some(true)
7575                } else if self.match_token(TokenType::Last) {
7576                    Some(false)
7577                } else {
7578                    return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
7579                }
7580            } else {
7581                None
7582            };
7583
7584            expressions.push(Ordered {
7585                this: expr,
7586                desc,
7587                nulls_first,
7588                explicit_asc,
7589                with_fill: None,
7590            });
7591
7592            if !self.match_token(TokenType::Comma) {
7593                break;
7594            }
7595        }
7596
7597        Ok(SortBy { expressions })
7598    }
7599
7600    /// Parse FOR UPDATE/SHARE locking clauses or FOR XML (T-SQL)
7601    /// Syntax: FOR UPDATE|SHARE|NO KEY UPDATE|KEY SHARE [OF tables] [NOWAIT|WAIT n|SKIP LOCKED]
7602    /// Also handles: LOCK IN SHARE MODE (MySQL)
7603    /// Also handles: FOR XML PATH|RAW|AUTO|EXPLICIT [, options...] (T-SQL)
7604    fn parse_locks_and_for_xml(&mut self) -> Result<(Vec<Lock>, Vec<Expression>)> {
7605        let mut locks = Vec::new();
7606        let mut for_xml = Vec::new();
7607
7608        loop {
7609            let (update, key) = if self.match_keywords(&[TokenType::For, TokenType::Update]) {
7610                // FOR UPDATE
7611                (
7612                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7613                        value: true,
7614                    }))),
7615                    None,
7616                )
7617            } else if self.check(TokenType::For) && self.check_next_identifier("XML") {
7618                // FOR XML (T-SQL) - parse XML options
7619                self.skip(); // consume FOR
7620                self.skip(); // consume XML
7621                for_xml = self.parse_for_xml_options()?;
7622                break; // FOR XML is always the last clause
7623            } else if self.check(TokenType::For) && self.check_next_identifier("SHARE") {
7624                // FOR SHARE
7625                self.skip(); // consume FOR
7626                self.skip(); // consume SHARE
7627                (None, None)
7628            } else if self.check_identifier("LOCK") && self.check_next(TokenType::In) {
7629                // LOCK IN SHARE MODE (MySQL) -> converted to FOR SHARE
7630                self.skip(); // consume LOCK
7631                self.skip(); // consume IN
7632                if self.match_identifier("SHARE") {
7633                    let _ = self.match_identifier("MODE");
7634                }
7635                (None, None)
7636            } else if self.check(TokenType::For) && self.check_next(TokenType::Key) {
7637                // FOR KEY SHARE (PostgreSQL)
7638                self.skip(); // consume FOR
7639                self.skip(); // consume KEY
7640                if !self.match_identifier("SHARE") {
7641                    break; // Not a valid lock clause
7642                }
7643                (
7644                    None,
7645                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7646                        value: true,
7647                    }))),
7648                )
7649            } else if self.check(TokenType::For) && self.check_next(TokenType::No) {
7650                // FOR NO KEY UPDATE (PostgreSQL)
7651                self.skip(); // consume FOR
7652                self.skip(); // consume NO
7653                if !self.match_identifier("KEY") || !self.match_token(TokenType::Update) {
7654                    break; // Not a valid lock clause
7655                }
7656                (
7657                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7658                        value: true,
7659                    }))),
7660                    Some(Box::new(Expression::Boolean(BooleanLiteral {
7661                        value: true,
7662                    }))),
7663                )
7664            } else {
7665                // No more lock clauses
7666                break;
7667            };
7668
7669            // Parse optional OF clause: OF table1, table2
7670            let expressions = if self.match_token(TokenType::Of) {
7671                let mut tables = Vec::new();
7672                loop {
7673                    // Parse table reference (can be schema.table or just table)
7674                    let table = self.parse_table_ref()?;
7675                    tables.push(Expression::Table(Box::new(table)));
7676                    if !self.match_token(TokenType::Comma) {
7677                        break;
7678                    }
7679                }
7680                tables
7681            } else {
7682                Vec::new()
7683            };
7684
7685            // Parse wait option: NOWAIT, WAIT n, or SKIP LOCKED
7686            // Following Python sqlglot convention:
7687            // - NOWAIT -> Boolean(true)
7688            // - SKIP LOCKED -> Boolean(false)
7689            // - WAIT n -> Literal (the number)
7690            let wait = if self.match_identifier("NOWAIT") {
7691                // NOWAIT -> represented as Boolean(true)
7692                Some(Box::new(Expression::Boolean(BooleanLiteral {
7693                    value: true,
7694                })))
7695            } else if self.match_identifier("WAIT") {
7696                // WAIT n -> wait = expression (the number/literal)
7697                Some(Box::new(self.parse_primary()?))
7698            } else if self.match_identifier("SKIP") && self.match_identifier("LOCKED") {
7699                // SKIP LOCKED -> represented as Boolean(false)
7700                Some(Box::new(Expression::Boolean(BooleanLiteral {
7701                    value: false,
7702                })))
7703            } else {
7704                None
7705            };
7706
7707            locks.push(Lock {
7708                update,
7709                expressions,
7710                wait,
7711                key,
7712            });
7713        }
7714
7715        Ok((locks, for_xml))
7716    }
7717
7718    /// Parse FOR XML options (T-SQL)
7719    /// Syntax: FOR XML PATH|RAW|AUTO|EXPLICIT [('element')] [, BINARY BASE64] [, ELEMENTS [XSINIL|ABSENT]] [, TYPE] [, ROOT('name')]
7720    fn parse_for_xml_options(&mut self) -> Result<Vec<Expression>> {
7721        let mut options = Vec::new();
7722
7723        loop {
7724            // Parse XML option: could be a known option (PATH, RAW, AUTO, EXPLICIT, BINARY, ELEMENTS, TYPE, ROOT)
7725            // or an XMLKeyValueOption like PATH('element')
7726            if let Some(opt) = self.parse_for_xml_single_option()? {
7727                options.push(opt);
7728            } else {
7729                break;
7730            }
7731
7732            // Check for comma to continue parsing more options
7733            if !self.match_token(TokenType::Comma) {
7734                break;
7735            }
7736        }
7737
7738        Ok(options)
7739    }
7740
7741    /// Parse a single FOR XML option
7742    fn parse_for_xml_single_option(&mut self) -> Result<Option<Expression>> {
7743        // Known XML modes: PATH, RAW, AUTO, EXPLICIT
7744        // Known options: BINARY BASE64, ELEMENTS [XSINIL|ABSENT], TYPE, ROOT('name')
7745
7746        // Try to match known patterns
7747        if self.match_identifier("PATH") {
7748            let expression = if self.match_token(TokenType::LParen) {
7749                let expr = self.parse_string()?;
7750                self.expect(TokenType::RParen)?;
7751                expr
7752            } else {
7753                None
7754            };
7755            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7756                this: Box::new(Expression::Var(Box::new(Var {
7757                    this: "PATH".to_string(),
7758                }))),
7759                expression: expression.map(|e| Box::new(e)),
7760            }))));
7761        }
7762
7763        if self.match_identifier("RAW") {
7764            let expression = if self.match_token(TokenType::LParen) {
7765                let expr = self.parse_string()?;
7766                self.expect(TokenType::RParen)?;
7767                expr
7768            } else {
7769                None
7770            };
7771            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7772                this: Box::new(Expression::Var(Box::new(Var {
7773                    this: "RAW".to_string(),
7774                }))),
7775                expression: expression.map(|e| Box::new(e)),
7776            }))));
7777        }
7778
7779        if self.match_identifier("AUTO") {
7780            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7781                this: Box::new(Expression::Var(Box::new(Var {
7782                    this: "AUTO".to_string(),
7783                }))),
7784                expression: None,
7785            }))));
7786        }
7787
7788        if self.match_identifier("EXPLICIT") {
7789            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7790                this: Box::new(Expression::Var(Box::new(Var {
7791                    this: "EXPLICIT".to_string(),
7792                }))),
7793                expression: None,
7794            }))));
7795        }
7796
7797        if self.match_identifier("TYPE") {
7798            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7799                this: Box::new(Expression::Var(Box::new(Var {
7800                    this: "TYPE".to_string(),
7801                }))),
7802                expression: None,
7803            }))));
7804        }
7805
7806        if self.match_identifier("BINARY") {
7807            // BINARY BASE64
7808            if self.match_identifier("BASE64") {
7809                return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7810                    this: Box::new(Expression::Var(Box::new(Var {
7811                        this: "BINARY BASE64".to_string(),
7812                    }))),
7813                    expression: None,
7814                }))));
7815            } else {
7816                return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7817                    this: Box::new(Expression::Var(Box::new(Var {
7818                        this: "BINARY".to_string(),
7819                    }))),
7820                    expression: None,
7821                }))));
7822            }
7823        }
7824
7825        if self.match_identifier("ELEMENTS") {
7826            // ELEMENTS [XSINIL|ABSENT]
7827            let suboption = if self.match_identifier("XSINIL") {
7828                Some("XSINIL".to_string())
7829            } else if self.match_identifier("ABSENT") {
7830                Some("ABSENT".to_string())
7831            } else {
7832                None
7833            };
7834            let option_name = match &suboption {
7835                Some(sub) => format!("ELEMENTS {}", sub),
7836                None => "ELEMENTS".to_string(),
7837            };
7838            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7839                this: Box::new(Expression::Var(Box::new(Var { this: option_name }))),
7840                expression: None,
7841            }))));
7842        }
7843
7844        if self.match_identifier("ROOT") {
7845            let expression = if self.match_token(TokenType::LParen) {
7846                let expr = self.parse_string()?;
7847                self.expect(TokenType::RParen)?;
7848                expr
7849            } else {
7850                None
7851            };
7852            return Ok(Some(Expression::QueryOption(Box::new(QueryOption {
7853                this: Box::new(Expression::Var(Box::new(Var {
7854                    this: "ROOT".to_string(),
7855                }))),
7856                expression: expression.map(|e| Box::new(e)),
7857            }))));
7858        }
7859
7860        // No more options recognized
7861        Ok(None)
7862    }
7863
7864    /// Parse CONNECT BY clause (Oracle hierarchical queries)
7865    /// Syntax: [START WITH condition] CONNECT BY [NOCYCLE] condition [START WITH condition]
7866    /// START WITH can appear before or after CONNECT BY
7867    fn parse_connect(&mut self) -> Result<Option<Connect>> {
7868        // Check for START WITH first (can appear before CONNECT BY)
7869        let start_before = if self.match_keywords(&[TokenType::Start, TokenType::With]) {
7870            Some(self.parse_expression()?)
7871        } else {
7872            None
7873        };
7874
7875        // Check for CONNECT BY
7876        if !self.match_keywords(&[TokenType::Connect, TokenType::By]) {
7877            if start_before.is_some() {
7878                return Err(self.parse_error("START WITH without CONNECT BY"));
7879            }
7880            return Ok(None);
7881        }
7882
7883        // Check for NOCYCLE
7884        let nocycle = self.match_token(TokenType::NoCycle);
7885
7886        // Parse the CONNECT BY condition with PRIOR support
7887        let connect = self.parse_connect_expression()?;
7888
7889        // START WITH can also appear after CONNECT BY
7890        let start = if start_before.is_some() {
7891            start_before
7892        } else if self.match_keywords(&[TokenType::Start, TokenType::With]) {
7893            Some(self.parse_expression()?)
7894        } else {
7895            None
7896        };
7897
7898        Ok(Some(Connect {
7899            start,
7900            connect,
7901            nocycle,
7902        }))
7903    }
7904
7905    /// Parse expression in CONNECT BY context, treating PRIOR as prefix operator
7906    fn parse_connect_expression(&mut self) -> Result<Expression> {
7907        self.parse_connect_or()
7908    }
7909
7910    /// Parse OR expression in CONNECT BY context
7911    fn parse_connect_or(&mut self) -> Result<Expression> {
7912        let mut left = self.parse_connect_and()?;
7913
7914        while self.match_token(TokenType::Or) {
7915            let right = self.parse_connect_and()?;
7916            left = Expression::Or(Box::new(BinaryOp::new(left, right)));
7917        }
7918
7919        Ok(Self::maybe_rebalance_boolean_chain(left, false))
7920    }
7921
7922    /// Parse AND expression in CONNECT BY context
7923    fn parse_connect_and(&mut self) -> Result<Expression> {
7924        let mut left = self.parse_connect_comparison()?;
7925
7926        while self.match_token(TokenType::And) {
7927            let right = self.parse_connect_comparison()?;
7928            left = Expression::And(Box::new(BinaryOp::new(left, right)));
7929        }
7930
7931        Ok(Self::maybe_rebalance_boolean_chain(left, true))
7932    }
7933
7934    /// Parse comparison in CONNECT BY context
7935    fn parse_connect_comparison(&mut self) -> Result<Expression> {
7936        let left = self.parse_connect_primary()?;
7937
7938        if self.match_token(TokenType::Eq) {
7939            let right = self.parse_connect_primary()?;
7940            return Ok(Expression::Eq(Box::new(BinaryOp::new(left, right))));
7941        }
7942        if self.match_token(TokenType::Neq) {
7943            let right = self.parse_connect_primary()?;
7944            return Ok(Expression::Neq(Box::new(BinaryOp::new(left, right))));
7945        }
7946        if self.match_token(TokenType::Lt) {
7947            let right = self.parse_connect_primary()?;
7948            return Ok(Expression::Lt(Box::new(BinaryOp::new(left, right))));
7949        }
7950        if self.match_token(TokenType::Lte) {
7951            let right = self.parse_connect_primary()?;
7952            return Ok(Expression::Lte(Box::new(BinaryOp::new(left, right))));
7953        }
7954        if self.match_token(TokenType::Gt) {
7955            let right = self.parse_connect_primary()?;
7956            return Ok(Expression::Gt(Box::new(BinaryOp::new(left, right))));
7957        }
7958        if self.match_token(TokenType::Gte) {
7959            let right = self.parse_connect_primary()?;
7960            return Ok(Expression::Gte(Box::new(BinaryOp::new(left, right))));
7961        }
7962
7963        Ok(left)
7964    }
7965
7966    /// Parse primary in CONNECT BY context with PRIOR support
7967    fn parse_connect_primary(&mut self) -> Result<Expression> {
7968        // Handle PRIOR as prefix operator
7969        if self.match_token(TokenType::Prior) {
7970            let expr = self.parse_primary()?;
7971            return Ok(Expression::Prior(Box::new(Prior { this: expr })));
7972        }
7973
7974        if let Some(connect_by_root) = self.try_parse_connect_by_root_expression()? {
7975            return Ok(connect_by_root);
7976        }
7977
7978        self.parse_primary()
7979    }
7980
7981    /// Parse Oracle CONNECT_BY_ROOT in either supported form:
7982    /// CONNECT_BY_ROOT col
7983    /// CONNECT_BY_ROOT(col)
7984    fn try_parse_connect_by_root_expression(&mut self) -> Result<Option<Expression>> {
7985        if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("CONNECT_BY_ROOT"))
7986        {
7987            return Ok(None);
7988        }
7989
7990        self.skip();
7991
7992        let this = if self.match_token(TokenType::LParen) {
7993            let expr = self.parse_expression()?;
7994            self.expect(TokenType::RParen)?;
7995            expr
7996        } else {
7997            self.parse_column()?.ok_or_else(|| {
7998                self.parse_error("Expected expression or column after CONNECT_BY_ROOT")
7999            })?
8000        };
8001
8002        Ok(Some(Expression::ConnectByRoot(Box::new(ConnectByRoot {
8003            this,
8004        }))))
8005    }
8006
8007    /// Parse MATCH_RECOGNIZE clause (Oracle/Snowflake/Presto/Trino pattern matching)
8008    /// MATCH_RECOGNIZE ( [PARTITION BY ...] [ORDER BY ...] [MEASURES ...] [rows] [after] PATTERN (...) DEFINE ... )
8009    fn parse_match_recognize(&mut self, source: Option<Expression>) -> Result<Expression> {
8010        self.expect(TokenType::LParen)?;
8011
8012        // PARTITION BY (optional)
8013        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
8014            Some(self.parse_expression_list()?)
8015        } else {
8016            None
8017        };
8018
8019        // ORDER BY (optional)
8020        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
8021            Some(self.parse_order_by()?.expressions)
8022        } else {
8023            None
8024        };
8025
8026        // MEASURES (optional)
8027        let measures = if self.match_token(TokenType::Measures) {
8028            Some(self.parse_match_recognize_measures()?)
8029        } else {
8030            None
8031        };
8032
8033        // Row semantics: ONE ROW PER MATCH / ALL ROWS PER MATCH
8034        let rows = self.parse_match_recognize_rows()?;
8035
8036        // AFTER MATCH SKIP
8037        let after = self.parse_match_recognize_after()?;
8038
8039        // PATTERN
8040        let pattern = if self.match_token(TokenType::Pattern) {
8041            Some(self.parse_match_recognize_pattern()?)
8042        } else {
8043            None
8044        };
8045
8046        // DEFINE
8047        let define = if self.match_token(TokenType::Define) {
8048            Some(self.parse_match_recognize_define()?)
8049        } else {
8050            None
8051        };
8052
8053        self.expect(TokenType::RParen)?;
8054
8055        // Alias is handled by the caller
8056
8057        Ok(Expression::MatchRecognize(Box::new(MatchRecognize {
8058            this: source.map(Box::new),
8059            partition_by,
8060            order_by,
8061            measures,
8062            rows,
8063            after,
8064            pattern,
8065            define,
8066            alias: None,
8067            alias_explicit_as: false,
8068        })))
8069    }
8070
8071    /// Parse MEASURES clause in MATCH_RECOGNIZE
8072    fn parse_match_recognize_measures(&mut self) -> Result<Vec<MatchRecognizeMeasure>> {
8073        let mut measures = Vec::new();
8074
8075        loop {
8076            // Check for RUNNING or FINAL
8077            let window_frame = if self.match_token(TokenType::Running) {
8078                Some(MatchRecognizeSemantics::Running)
8079            } else if self.match_token(TokenType::Final) {
8080                Some(MatchRecognizeSemantics::Final)
8081            } else {
8082                None
8083            };
8084
8085            let mut expr = self.parse_expression()?;
8086
8087            // Handle AS alias for measures
8088            if self.match_token(TokenType::As) {
8089                let alias = Identifier::new(self.expect_identifier()?);
8090                expr = Expression::Alias(Box::new(Alias::new(expr, alias)));
8091            }
8092
8093            measures.push(MatchRecognizeMeasure {
8094                this: expr,
8095                window_frame,
8096            });
8097
8098            if !self.match_token(TokenType::Comma) {
8099                break;
8100            }
8101        }
8102
8103        Ok(measures)
8104    }
8105
8106    /// Parse row semantics in MATCH_RECOGNIZE
8107    fn parse_match_recognize_rows(&mut self) -> Result<Option<MatchRecognizeRows>> {
8108        // ONE ROW PER MATCH
8109        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ONE") {
8110            self.skip(); // consume ONE
8111            if !self.match_token(TokenType::Row) {
8112                return Err(self.parse_error("Expected ROW after ONE"));
8113            }
8114            if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PER")) {
8115                return Err(self.parse_error("Expected PER after ONE ROW"));
8116            }
8117            self.skip(); // consume PER
8118            if !self.match_token(TokenType::Match) {
8119                return Err(self.parse_error("Expected MATCH after ONE ROW PER"));
8120            }
8121            return Ok(Some(MatchRecognizeRows::OneRowPerMatch));
8122        }
8123
8124        // ALL ROWS PER MATCH [variants]
8125        if self.match_token(TokenType::All) {
8126            if !self.match_token(TokenType::Rows) {
8127                return Err(self.parse_error("Expected ROWS after ALL"));
8128            }
8129            if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PER")) {
8130                return Err(self.parse_error("Expected PER after ALL ROWS"));
8131            }
8132            self.skip(); // consume PER
8133            if !self.match_token(TokenType::Match) {
8134                return Err(self.parse_error("Expected MATCH after ALL ROWS PER"));
8135            }
8136
8137            // Check for optional modifiers
8138            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SHOW") {
8139                self.skip();
8140                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EMPTY") {
8141                    self.skip();
8142                    if self.check(TokenType::Var)
8143                        && self.peek().text.eq_ignore_ascii_case("MATCHES")
8144                    {
8145                        self.skip();
8146                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchShowEmptyMatches));
8147                    }
8148                }
8149                return Err(self.parse_error("Expected EMPTY MATCHES after SHOW"));
8150            }
8151
8152            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OMIT") {
8153                self.skip();
8154                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EMPTY") {
8155                    self.skip();
8156                    if self.check(TokenType::Var)
8157                        && self.peek().text.eq_ignore_ascii_case("MATCHES")
8158                    {
8159                        self.skip();
8160                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchOmitEmptyMatches));
8161                    }
8162                }
8163                return Err(self.parse_error("Expected EMPTY MATCHES after OMIT"));
8164            }
8165
8166            if self.match_token(TokenType::With) {
8167                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("UNMATCHED")
8168                {
8169                    self.skip();
8170                    if self.match_token(TokenType::Rows) {
8171                        return Ok(Some(MatchRecognizeRows::AllRowsPerMatchWithUnmatchedRows));
8172                    }
8173                }
8174                return Err(self.parse_error("Expected UNMATCHED ROWS after WITH"));
8175            }
8176
8177            return Ok(Some(MatchRecognizeRows::AllRowsPerMatch));
8178        }
8179
8180        Ok(None)
8181    }
8182
8183    /// Parse AFTER MATCH SKIP clause in MATCH_RECOGNIZE
8184    fn parse_match_recognize_after(&mut self) -> Result<Option<MatchRecognizeAfter>> {
8185        if !self.match_token(TokenType::After) {
8186            return Ok(None);
8187        }
8188
8189        if !self.match_token(TokenType::Match) {
8190            return Err(self.parse_error("Expected MATCH after AFTER"));
8191        }
8192
8193        // Check for SKIP (it might be an identifier)
8194        if !(self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SKIP")) {
8195            return Err(self.parse_error("Expected SKIP after AFTER MATCH"));
8196        }
8197        self.skip(); // consume SKIP
8198
8199        // PAST LAST ROW
8200        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("PAST") {
8201            self.skip();
8202            if self.match_token(TokenType::Last) {
8203                if self.match_token(TokenType::Row) {
8204                    return Ok(Some(MatchRecognizeAfter::PastLastRow));
8205                }
8206            }
8207            return Err(self.parse_error("Expected LAST ROW after PAST"));
8208        }
8209
8210        // TO NEXT ROW / TO FIRST x / TO LAST x
8211        if self.match_token(TokenType::To) {
8212            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("NEXT") {
8213                self.skip();
8214                if self.match_token(TokenType::Row) {
8215                    return Ok(Some(MatchRecognizeAfter::ToNextRow));
8216                }
8217                return Err(self.parse_error("Expected ROW after NEXT"));
8218            }
8219
8220            if self.match_token(TokenType::First) {
8221                let name = self.expect_identifier()?;
8222                return Ok(Some(MatchRecognizeAfter::ToFirst(Identifier::new(name))));
8223            }
8224
8225            if self.match_token(TokenType::Last) {
8226                let name = self.expect_identifier()?;
8227                return Ok(Some(MatchRecognizeAfter::ToLast(Identifier::new(name))));
8228            }
8229
8230            return Err(self.parse_error("Expected NEXT ROW, FIRST x, or LAST x after TO"));
8231        }
8232
8233        Err(self.parse_error("Expected PAST LAST ROW or TO ... after AFTER MATCH SKIP"))
8234    }
8235
8236    /// Parse PATTERN clause in MATCH_RECOGNIZE using bracket counting
8237    fn parse_match_recognize_pattern(&mut self) -> Result<String> {
8238        self.expect(TokenType::LParen)?;
8239
8240        let mut depth = 1;
8241        let mut pattern = String::new();
8242
8243        while depth > 0 && !self.is_at_end() {
8244            let token = self.advance();
8245            match token.token_type {
8246                TokenType::LParen => {
8247                    depth += 1;
8248                    pattern.push('(');
8249                }
8250                TokenType::RParen => {
8251                    depth -= 1;
8252                    if depth > 0 {
8253                        pattern.push(')');
8254                    }
8255                }
8256                _ => {
8257                    // Pattern quantifiers (+, *, ?, {n,m}) should not have a space before them
8258                    let is_quantifier = matches!(token.text.as_str(), "+" | "*" | "?")
8259                        || token.text.starts_with('{');
8260
8261                    if !pattern.is_empty()
8262                        && !pattern.ends_with('(')
8263                        && !pattern.ends_with(' ')
8264                        && !is_quantifier
8265                    {
8266                        pattern.push(' ');
8267                    }
8268                    pattern.push_str(&token.text);
8269                }
8270            }
8271        }
8272
8273        if depth > 0 {
8274            return Err(self.parse_error("Unclosed parenthesis in PATTERN clause"));
8275        }
8276
8277        Ok(pattern.trim().to_string())
8278    }
8279
8280    /// Parse DEFINE clause in MATCH_RECOGNIZE
8281    fn parse_match_recognize_define(&mut self) -> Result<Vec<(Identifier, Expression)>> {
8282        let mut definitions = Vec::new();
8283
8284        loop {
8285            let name = Identifier::new(self.expect_identifier()?);
8286            self.expect(TokenType::As)?;
8287            let expr = self.parse_expression()?;
8288
8289            definitions.push((name, expr));
8290
8291            if !self.match_token(TokenType::Comma) {
8292                break;
8293            }
8294        }
8295
8296        Ok(definitions)
8297    }
8298
8299    /// Parse LATERAL VIEW clauses (Hive/Spark)
8300    /// Syntax: LATERAL VIEW [OUTER] generator_function(args) table_alias AS col1 [, col2, ...]
8301    fn parse_lateral_views(&mut self) -> Result<Vec<LateralView>> {
8302        let mut views = Vec::new();
8303
8304        while self.match_keywords(&[TokenType::Lateral, TokenType::View]) {
8305            // Check for OUTER keyword
8306            let outer = self.match_token(TokenType::Outer);
8307
8308            // Parse the generator function (EXPLODE, POSEXPLODE, INLINE, etc.)
8309            // This is a function call expression
8310            let this = self.parse_primary()?;
8311
8312            // Parse table alias (comes before AS)
8313            let table_alias = if self.check(TokenType::Var) && !self.check_keyword() {
8314                Some(Identifier::new(self.expect_identifier()?))
8315            } else {
8316                None
8317            };
8318
8319            // Parse column aliases after AS keyword
8320            // Supports both: AS a, b and AS (a, b)
8321            let column_aliases = if self.match_token(TokenType::As) {
8322                let mut aliases = Vec::new();
8323                // Check for parenthesized alias list: AS ("a", "b")
8324                if self.match_token(TokenType::LParen) {
8325                    loop {
8326                        aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
8327                        if !self.match_token(TokenType::Comma) {
8328                            break;
8329                        }
8330                    }
8331                    self.expect(TokenType::RParen)?;
8332                } else {
8333                    // Non-parenthesized aliases: AS a, b, c
8334                    // Use expect_identifier_or_keyword because aliases like "key", "value", "pos" may be keywords
8335                    loop {
8336                        aliases.push(Identifier::new(self.expect_identifier_or_keyword()?));
8337                        if !self.match_token(TokenType::Comma) {
8338                            break;
8339                        }
8340                        // Check if next token is still an identifier or keyword (column alias)
8341                        // vs starting a new LATERAL VIEW or other clause
8342                        if !self.is_identifier_or_keyword_token() {
8343                            break;
8344                        }
8345                        // Check for keywords that would end the column list
8346                        if self.peek().token_type == TokenType::Lateral
8347                            || self.peek().token_type == TokenType::Where
8348                            || self.peek().token_type == TokenType::Group
8349                            || self.peek().token_type == TokenType::Having
8350                            || self.peek().token_type == TokenType::Order
8351                            || self.peek().token_type == TokenType::Limit
8352                        {
8353                            break;
8354                        }
8355                    }
8356                }
8357                aliases
8358            } else {
8359                Vec::new()
8360            };
8361
8362            views.push(LateralView {
8363                this,
8364                table_alias,
8365                column_aliases,
8366                outer,
8367            });
8368        }
8369
8370        Ok(views)
8371    }
8372
8373    /// Parse named windows (WINDOW w AS (...), ...)
8374    fn parse_named_windows(&mut self) -> Result<Vec<NamedWindow>> {
8375        let mut windows = Vec::new();
8376
8377        loop {
8378            let name = self.expect_identifier()?;
8379            self.expect(TokenType::As)?;
8380            self.expect(TokenType::LParen)?;
8381
8382            // Parse optional base window name reference (e.g., w1 AS (w0 ORDER BY ...))
8383            let window_name = if (self.check(TokenType::Identifier)
8384                || self.check(TokenType::Var)
8385                || self.check(TokenType::QuotedIdentifier))
8386                && !self.check(TokenType::Partition)
8387                && !self.check(TokenType::Order)
8388                && self.peek_nth(1).map_or(true, |t| {
8389                    matches!(
8390                        t.token_type,
8391                        TokenType::Partition
8392                            | TokenType::Order
8393                            | TokenType::Rows
8394                            | TokenType::Range
8395                            | TokenType::Groups
8396                            | TokenType::RParen
8397                            | TokenType::Comma
8398                    )
8399                }) {
8400                Some(self.expect_identifier()?)
8401            } else {
8402                None
8403            };
8404
8405            // Parse window specification
8406            let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
8407                Some(self.parse_expression_list()?)
8408            } else {
8409                None
8410            };
8411
8412            let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
8413                Some(self.parse_order_by()?)
8414            } else {
8415                None
8416            };
8417
8418            let frame = self.parse_window_frame()?;
8419
8420            self.expect(TokenType::RParen)?;
8421
8422            windows.push(NamedWindow {
8423                name: Identifier::new(name),
8424                spec: Over {
8425                    window_name: window_name.map(|n| Identifier::new(n)),
8426                    partition_by: partition_by.unwrap_or_default(),
8427                    order_by: order_by.map(|o| o.expressions).unwrap_or_default(),
8428                    frame,
8429                    alias: None,
8430                },
8431            });
8432
8433            if !self.match_token(TokenType::Comma) {
8434                break;
8435            }
8436        }
8437
8438        Ok(windows)
8439    }
8440
8441    /// Parse query hint /*+ ... */
8442    fn parse_hint(&mut self) -> Result<Hint> {
8443        let token = self.advance();
8444        let hint_text = token.text.clone();
8445
8446        // For now, parse as raw hint text
8447        // More sophisticated parsing can be added later
8448        let expressions = if hint_text.is_empty() {
8449            Vec::new()
8450        } else {
8451            vec![HintExpression::Raw(hint_text)]
8452        };
8453
8454        Ok(Hint { expressions })
8455    }
8456
8457    /// Parse SAMPLE / TABLESAMPLE / USING SAMPLE clause
8458    fn parse_sample_clause(&mut self) -> Result<Option<Sample>> {
8459        // Check for USING SAMPLE (DuckDB), SAMPLE, or TABLESAMPLE
8460        let is_using_sample = if self.check(TokenType::Using)
8461            && self.current + 1 < self.tokens.len()
8462            && self.tokens[self.current + 1].token_type == TokenType::Sample
8463        {
8464            self.skip(); // consume USING
8465            self.skip(); // consume SAMPLE
8466            true
8467        } else {
8468            false
8469        };
8470
8471        let use_sample_keyword = if is_using_sample {
8472            // USING SAMPLE acts like SAMPLE
8473            true
8474        } else if self.match_token(TokenType::Sample) {
8475            true
8476        } else if self.match_token(TokenType::TableSample) {
8477            false
8478        } else {
8479            return Ok(None);
8480        };
8481
8482        // Parse sampling method if specified (BERNOULLI, SYSTEM, BLOCK, ROW, RESERVOIR)
8483        let (method, method_before_size, explicit_method) =
8484            if self.match_token(TokenType::Bernoulli) {
8485                (SampleMethod::Bernoulli, true, true)
8486            } else if self.match_token(TokenType::System) {
8487                (SampleMethod::System, true, true)
8488            } else if self.match_token(TokenType::Block) {
8489                (SampleMethod::Block, true, true)
8490            } else if self.match_token(TokenType::Row) {
8491                (SampleMethod::Row, true, true)
8492            } else if self.check_identifier("RESERVOIR") {
8493                self.skip();
8494                (SampleMethod::Reservoir, true, true)
8495            } else {
8496                // Default to BERNOULLI for both SAMPLE and TABLESAMPLE
8497                // This matches Python SQLGlot's normalization behavior
8498                (SampleMethod::Bernoulli, false, false)
8499            };
8500
8501        // Parse size (can be in parentheses)
8502        let has_paren = self.match_token(TokenType::LParen);
8503
8504        // Check for BUCKET syntax: TABLESAMPLE (BUCKET 1 OUT OF 5 ON x)
8505        if self.match_identifier("BUCKET") {
8506            let bucket_numerator = self.parse_primary()?;
8507            self.match_identifier("OUT");
8508            self.match_token(TokenType::Of); // OF is a keyword token
8509            let bucket_denominator = self.parse_primary()?;
8510            let bucket_field = if self.match_token(TokenType::On) {
8511                Some(Box::new(self.parse_primary()?))
8512            } else {
8513                None
8514            };
8515            if has_paren {
8516                self.expect(TokenType::RParen)?;
8517            }
8518            return Ok(Some(Sample {
8519                method: SampleMethod::Bucket,
8520                size: bucket_numerator.clone(),
8521                seed: None,
8522                offset: None,
8523                unit_after_size: false,
8524                use_sample_keyword,
8525                explicit_method: true,     // BUCKET is always explicit
8526                method_before_size: false, // BUCKET appears inside parens
8527                use_seed_keyword: false,
8528                bucket_numerator: Some(Box::new(bucket_numerator)),
8529                bucket_denominator: Some(Box::new(bucket_denominator)),
8530                bucket_field,
8531                is_using_sample,
8532                is_percent: false,
8533                suppress_method_output: false,
8534            }));
8535        }
8536
8537        // Use parse_unary to avoid consuming PERCENT as modulo operator
8538        let size = self.parse_unary()?;
8539
8540        // Check for PERCENT/ROWS suffix after size (if not already part of the number)
8541        // Both "%" and "PERCENT" tokens map to TokenType::Percent - accept both as PERCENT modifier
8542        let (method, unit_after_size, is_percent) = if self.check(TokenType::Percent) {
8543            self.skip(); // consume PERCENT or %
8544                         // If method was already explicitly specified (e.g., SYSTEM), keep it
8545                         // PERCENT here is just the unit, not the sampling method
8546            if method_before_size {
8547                (method, true, true)
8548            } else {
8549                (SampleMethod::Percent, true, true)
8550            }
8551        } else if self.match_token(TokenType::Rows) {
8552            // If method was already explicitly specified, keep it
8553            if method_before_size {
8554                (method, true, false)
8555            } else {
8556                (SampleMethod::Row, true, false)
8557            }
8558        } else {
8559            // No explicit unit after size - preserve the original method
8560            (method, false, false)
8561        };
8562
8563        if has_paren {
8564            self.expect(TokenType::RParen)?;
8565        }
8566
8567        // DuckDB USING SAMPLE: method and optional seed can come in parens after size
8568        // e.g., "10 PERCENT (bernoulli)" or "10% (system, 377)"
8569        // DuckDB USING SAMPLE: method and optional seed can come in parens after size
8570        // e.g., "10 PERCENT (bernoulli)" or "10% (system, 377)"
8571        let (method, seed, use_seed_keyword, explicit_method) =
8572            if is_using_sample && self.check(TokenType::LParen) {
8573                self.skip(); // consume LParen
8574                             // Parse method name as identifier or keyword token
8575                             // BERNOULLI, SYSTEM, RESERVOIR can be tokenized as keywords, not identifiers
8576                let method_from_parens =
8577                    if self.check_identifier("BERNOULLI") || self.check(TokenType::Bernoulli) {
8578                        self.skip();
8579                        Some(SampleMethod::Bernoulli)
8580                    } else if self.check_identifier("SYSTEM") || self.check(TokenType::System) {
8581                        self.skip();
8582                        Some(SampleMethod::System)
8583                    } else if self.check_identifier("RESERVOIR") {
8584                        self.skip();
8585                        Some(SampleMethod::Reservoir)
8586                    } else {
8587                        None
8588                    };
8589                // Optional seed after comma
8590                let seed = if self.match_token(TokenType::Comma) {
8591                    Some(self.parse_expression()?)
8592                } else {
8593                    None
8594                };
8595                self.expect(TokenType::RParen)?;
8596                let final_method = method_from_parens.unwrap_or(method);
8597                (final_method, seed, false, true)
8598            } else {
8599                // Parse optional SEED / REPEATABLE
8600                let (seed, use_seed_keyword) = if self.match_token(TokenType::Seed) {
8601                    self.expect(TokenType::LParen)?;
8602                    let seed_value = self.parse_expression()?;
8603                    self.expect(TokenType::RParen)?;
8604                    (Some(seed_value), true)
8605                } else if self.match_token(TokenType::Repeatable) {
8606                    self.expect(TokenType::LParen)?;
8607                    let seed_value = self.parse_expression()?;
8608                    self.expect(TokenType::RParen)?;
8609                    (Some(seed_value), false)
8610                } else {
8611                    (None, false)
8612                };
8613                let explicit_method = explicit_method || unit_after_size;
8614                (method, seed, use_seed_keyword, explicit_method)
8615            };
8616
8617        // For DuckDB USING SAMPLE: apply default methods
8618        // - bare number -> RESERVOIR, ROWS
8619        // - percent -> SYSTEM, PERCENT
8620        let (method, unit_after_size) = if is_using_sample && !explicit_method {
8621            // No explicit method - apply defaults
8622            (SampleMethod::Reservoir, false) // default: RESERVOIR with ROWS
8623        } else if is_using_sample && unit_after_size && !method_before_size {
8624            // Unit was specified after size (e.g., "10 PERCENT") but no method before
8625            // Check if method was set in post-parens
8626            if matches!(method, SampleMethod::Percent) {
8627                // "10%" or "10 PERCENT" without method -> SYSTEM
8628                (SampleMethod::System, true)
8629            } else if matches!(method, SampleMethod::Row) {
8630                // "50 ROWS" without method -> RESERVOIR
8631                (SampleMethod::Reservoir, true)
8632            } else {
8633                (method, unit_after_size)
8634            }
8635        } else {
8636            (method, unit_after_size)
8637        };
8638
8639        // method_before_size: true for USING SAMPLE - we normalize to method-before-size format
8640        // e.g., "10 PERCENT (bernoulli)" becomes "BERNOULLI (10 PERCENT)"
8641        Ok(Some(Sample {
8642            method,
8643            size,
8644            seed,
8645            offset: None,
8646            unit_after_size,
8647            use_sample_keyword,
8648            explicit_method: true,    // For USING SAMPLE, always explicit
8649            method_before_size: true, // Normalize to method-before-size format
8650            use_seed_keyword,
8651            bucket_numerator: None,
8652            bucket_denominator: None,
8653            bucket_field: None,
8654            is_using_sample,
8655            is_percent,
8656            suppress_method_output: false,
8657        }))
8658    }
8659
8660    /// Parse table-level TABLESAMPLE/SAMPLE: TABLESAMPLE/SAMPLE METHOD(size [PERCENT|ROWS])
8661    /// e.g., TABLESAMPLE RESERVOIR(20%), SAMPLE BERNOULLI(10 PERCENT), SAMPLE ROW(0)
8662    fn parse_table_level_sample(&mut self) -> Result<Option<Sample>> {
8663        // Accept both TABLESAMPLE and SAMPLE (Snowflake supports both)
8664        let use_sample_keyword = if self.match_token(TokenType::Sample) {
8665            true
8666        } else if self.match_token(TokenType::TableSample) {
8667            false
8668        } else {
8669            return Ok(None);
8670        };
8671        // Track which keyword was used for identity output
8672        let _ = use_sample_keyword; // Used below for is_using_sample field
8673
8674        // Teradata: SAMPLE 5 or SAMPLE 0.33, .25, .1 (no parentheses)
8675        if matches!(
8676            self.config.dialect,
8677            Some(crate::dialects::DialectType::Teradata)
8678        ) && use_sample_keyword
8679            && !self.check(TokenType::LParen)
8680        {
8681            let mut expressions = vec![self.parse_unary()?];
8682            while self.match_token(TokenType::Comma) {
8683                expressions.push(self.parse_unary()?);
8684            }
8685            let size = if expressions.len() == 1 {
8686                expressions.into_iter().next().unwrap()
8687            } else {
8688                Expression::Tuple(Box::new(Tuple { expressions }))
8689            };
8690            return Ok(Some(Sample {
8691                method: SampleMethod::Percent,
8692                size,
8693                seed: None,
8694                offset: None,
8695                unit_after_size: false,
8696                use_sample_keyword,
8697                explicit_method: false,
8698                method_before_size: false,
8699                use_seed_keyword: false,
8700                bucket_numerator: None,
8701                bucket_denominator: None,
8702                bucket_field: None,
8703                is_using_sample: false,
8704                is_percent: false,
8705                suppress_method_output: false,
8706            }));
8707        }
8708
8709        // ClickHouse: SAMPLE 0.1 [OFFSET 0.2] (no parentheses)
8710        if matches!(
8711            self.config.dialect,
8712            Some(crate::dialects::DialectType::ClickHouse)
8713        ) && use_sample_keyword
8714            && !self.check(TokenType::LParen)
8715        {
8716            let size = self.parse_expression()?;
8717            let offset = if self.match_token(TokenType::Offset) {
8718                Some(self.parse_expression()?)
8719            } else {
8720                None
8721            };
8722            return Ok(Some(Sample {
8723                method: SampleMethod::Bernoulli,
8724                size,
8725                seed: None,
8726                offset,
8727                unit_after_size: false,
8728                use_sample_keyword,
8729                explicit_method: false,
8730                method_before_size: false,
8731                use_seed_keyword: false,
8732                bucket_numerator: None,
8733                bucket_denominator: None,
8734                bucket_field: None,
8735                is_using_sample: false,
8736                is_percent: false,
8737                suppress_method_output: false,
8738            }));
8739        }
8740
8741        // Parse method name (optional for table-level TABLESAMPLE)
8742        let (method, explicit_method, method_before_size) = if self.check_identifier("RESERVOIR") {
8743            self.skip();
8744            (SampleMethod::Reservoir, true, true)
8745        } else if self.match_token(TokenType::Bernoulli) {
8746            (SampleMethod::Bernoulli, true, true)
8747        } else if self.match_token(TokenType::System) {
8748            (SampleMethod::System, true, true)
8749        } else if self.match_token(TokenType::Block) {
8750            (SampleMethod::Block, true, true)
8751        } else if self.match_token(TokenType::Row) {
8752            (SampleMethod::Row, true, true)
8753        } else {
8754            // No explicit method - default to Bernoulli internally but track as not explicit
8755            (SampleMethod::Bernoulli, false, false)
8756        };
8757
8758        // Parse (size [PERCENT|ROWS])
8759        self.expect(TokenType::LParen)?;
8760
8761        // Check for BUCKET syntax: TABLESAMPLE (BUCKET 1 OUT OF 5 [ON col])
8762        if self.match_identifier("BUCKET") {
8763            let bucket_numerator = self.parse_primary()?;
8764            self.match_identifier("OUT");
8765            self.match_token(TokenType::Of);
8766            let bucket_denominator = self.parse_primary()?;
8767            let bucket_field = if self.match_token(TokenType::On) {
8768                Some(Box::new(self.parse_primary()?))
8769            } else {
8770                None
8771            };
8772            self.expect(TokenType::RParen)?;
8773            return Ok(Some(Sample {
8774                method: SampleMethod::Bucket,
8775                size: bucket_numerator.clone(),
8776                seed: None,
8777                offset: None,
8778                unit_after_size: false,
8779                use_sample_keyword,
8780                explicit_method: true,
8781                method_before_size: false,
8782                use_seed_keyword: false,
8783                bucket_numerator: Some(Box::new(bucket_numerator)),
8784                bucket_denominator: Some(Box::new(bucket_denominator)),
8785                bucket_field,
8786                is_using_sample: false,
8787                is_percent: false,
8788                suppress_method_output: false,
8789            }));
8790        }
8791
8792        let size = self.parse_unary()?;
8793
8794        // Check for PERCENT/ROWS suffix or % symbol
8795        let (method, unit_after_size, is_percent) =
8796            if self.check(TokenType::Percent) && self.peek().text.eq_ignore_ascii_case("PERCENT") {
8797                self.skip();
8798                // If no explicit method, use Percent to represent "PERCENT" unit
8799                if explicit_method {
8800                    (method, true, true)
8801                } else {
8802                    (SampleMethod::Percent, true, true)
8803                }
8804            } else if self.match_token(TokenType::Rows) {
8805                // If no explicit method, use Row to represent "ROWS" unit
8806                if explicit_method {
8807                    (method, true, false)
8808                } else {
8809                    (SampleMethod::Row, true, false)
8810                }
8811            } else if self.check(TokenType::Percent) && self.peek().text == "%" {
8812                // 20% -> consume the %, treat as PERCENT unit
8813                self.skip();
8814                if explicit_method {
8815                    (method, true, true)
8816                } else {
8817                    (SampleMethod::Percent, true, true)
8818                }
8819            } else {
8820                (method, false, false)
8821            };
8822
8823        self.expect(TokenType::RParen)?;
8824
8825        // Optional SEED/REPEATABLE
8826        let (seed, use_seed_keyword) = if self.match_token(TokenType::Seed) {
8827            self.expect(TokenType::LParen)?;
8828            let seed_value = self.parse_expression()?;
8829            self.expect(TokenType::RParen)?;
8830            (Some(seed_value), true)
8831        } else if self.match_token(TokenType::Repeatable) {
8832            self.expect(TokenType::LParen)?;
8833            let seed_value = self.parse_expression()?;
8834            self.expect(TokenType::RParen)?;
8835            (Some(seed_value), false)
8836        } else {
8837            (None, false)
8838        };
8839
8840        Ok(Some(Sample {
8841            method,
8842            size,
8843            seed,
8844            offset: None,
8845            unit_after_size,
8846            use_sample_keyword,
8847            explicit_method,
8848            method_before_size,
8849            use_seed_keyword,
8850            bucket_numerator: None,
8851            bucket_denominator: None,
8852            bucket_field: None,
8853            is_using_sample: false, // table-level uses TABLESAMPLE/SAMPLE keyword, not USING SAMPLE
8854            is_percent,
8855            suppress_method_output: false,
8856        }))
8857    }
8858
8859    /// Parse set operations (UNION, INTERSECT, EXCEPT)
8860    fn parse_set_operation(&mut self, left: Expression) -> Result<Expression> {
8861        let mut result = left;
8862        let mut found_set_op = false;
8863
8864        loop {
8865            // Check for BigQuery set operation modifiers BEFORE the set operation keyword
8866            // Pattern: SELECT ... [INNER|LEFT|RIGHT|FULL] UNION/INTERSECT/EXCEPT ...
8867            let (side, kind) = self.parse_set_operation_side_kind();
8868
8869            // Capture leading comments from the set operation keyword token (e.g., /*x*/ before UNION).
8870            // These comments appeared on a new line between the left SELECT and the set operation keyword.
8871            let set_op_leading_comments = if self.check(TokenType::Union)
8872                || self.check(TokenType::Intersect)
8873                || self.check(TokenType::Except)
8874            {
8875                self.current_leading_comments().to_vec()
8876            } else {
8877                Vec::new()
8878            };
8879
8880            // Wrap left expression with comments if needed
8881            let left = if !set_op_leading_comments.is_empty() {
8882                Expression::Annotated(Box::new(Annotated {
8883                    this: result,
8884                    trailing_comments: set_op_leading_comments,
8885                }))
8886            } else {
8887                result
8888            };
8889
8890            if self.match_token(TokenType::Union) {
8891                let all = self.match_token(TokenType::All);
8892                let distinct = if !all {
8893                    self.match_token(TokenType::Distinct)
8894                } else {
8895                    false
8896                };
8897
8898                let (by_name, strict, corresponding, on_columns) =
8899                    self.parse_set_operation_corresponding()?;
8900
8901                let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8902                    Some("INNER".to_string())
8903                } else {
8904                    kind
8905                };
8906
8907                let right = self.parse_select_or_paren_select()?;
8908                result = Expression::Union(Box::new(Union {
8909                    left,
8910                    right,
8911                    all,
8912                    distinct,
8913                    with: None,
8914                    order_by: None,
8915                    limit: None,
8916                    offset: None,
8917                    distribute_by: None,
8918                    sort_by: None,
8919                    cluster_by: None,
8920                    by_name,
8921                    side,
8922                    kind,
8923                    corresponding,
8924                    strict,
8925                    on_columns,
8926                }));
8927                found_set_op = true;
8928            } else if self.match_token(TokenType::Intersect) {
8929                let all = self.match_token(TokenType::All);
8930                let distinct = if !all {
8931                    self.match_token(TokenType::Distinct)
8932                } else {
8933                    false
8934                };
8935
8936                let (by_name, strict, corresponding, on_columns) =
8937                    self.parse_set_operation_corresponding()?;
8938
8939                let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8940                    Some("INNER".to_string())
8941                } else {
8942                    kind
8943                };
8944
8945                let right = self.parse_select_or_paren_select()?;
8946                result = Expression::Intersect(Box::new(Intersect {
8947                    left,
8948                    right,
8949                    all,
8950                    distinct,
8951                    with: None,
8952                    order_by: None,
8953                    limit: None,
8954                    offset: None,
8955                    distribute_by: None,
8956                    sort_by: None,
8957                    cluster_by: None,
8958                    by_name,
8959                    side,
8960                    kind,
8961                    corresponding,
8962                    strict,
8963                    on_columns,
8964                }));
8965                found_set_op = true;
8966            } else if self.match_token(TokenType::Except) {
8967                let all = self.match_token(TokenType::All);
8968                let distinct = if !all {
8969                    self.match_token(TokenType::Distinct)
8970                } else {
8971                    false
8972                };
8973
8974                let (by_name, strict, corresponding, on_columns) =
8975                    self.parse_set_operation_corresponding()?;
8976
8977                let kind = if corresponding && !strict && side.is_none() && kind.is_none() {
8978                    Some("INNER".to_string())
8979                } else {
8980                    kind
8981                };
8982
8983                let right = self.parse_select_or_paren_select()?;
8984                result = Expression::Except(Box::new(Except {
8985                    left,
8986                    right,
8987                    all,
8988                    distinct,
8989                    with: None,
8990                    order_by: None,
8991                    limit: None,
8992                    offset: None,
8993                    distribute_by: None,
8994                    sort_by: None,
8995                    cluster_by: None,
8996                    by_name,
8997                    side,
8998                    kind,
8999                    corresponding,
9000                    strict,
9001                    on_columns,
9002                }));
9003                found_set_op = true;
9004            } else if side.is_some() || kind.is_some() {
9005                return Err(self.parse_error(
9006                    "Expected UNION, INTERSECT, or EXCEPT after set operation modifier",
9007                ));
9008            } else {
9009                result = left;
9010                break;
9011            }
9012        }
9013
9014        // Parse ORDER BY, LIMIT, OFFSET for the outermost set operation
9015        if found_set_op {
9016            self.parse_set_operation_modifiers(&mut result)?;
9017        }
9018        Ok(result)
9019    }
9020
9021    /// Parse BigQuery set operation side (LEFT, RIGHT, FULL) and kind (INNER)
9022    /// These modifiers appear BEFORE the UNION/INTERSECT/EXCEPT keyword
9023    fn parse_set_operation_side_kind(&mut self) -> (Option<String>, Option<String>) {
9024        let mut side = None;
9025        let mut kind = None;
9026
9027        // Check for side: LEFT, RIGHT, FULL (reusing join side tokens)
9028        if self.check(TokenType::Left)
9029            || self.check(TokenType::Right)
9030            || self.check(TokenType::Full)
9031        {
9032            // Only consume if followed by UNION/INTERSECT/EXCEPT (or INNER which would be followed by them)
9033            let saved = self.current;
9034            let side_token = self.advance();
9035            let side_text = side_token.text.to_ascii_uppercase();
9036
9037            // Check if followed by set operation or INNER
9038            if self.check(TokenType::Union)
9039                || self.check(TokenType::Intersect)
9040                || self.check(TokenType::Except)
9041                || self.check(TokenType::Inner)
9042            {
9043                side = Some(side_text);
9044            } else {
9045                // Not a set operation modifier, backtrack
9046                self.current = saved;
9047                return (None, None);
9048            }
9049        }
9050
9051        // Check for kind: INNER
9052        if self.check(TokenType::Inner) {
9053            let saved = self.current;
9054            self.skip(); // consume INNER
9055
9056            // Check if followed by set operation
9057            if self.check(TokenType::Union)
9058                || self.check(TokenType::Intersect)
9059                || self.check(TokenType::Except)
9060            {
9061                kind = Some("INNER".to_string());
9062            } else {
9063                // Not a set operation modifier, backtrack
9064                self.current = saved;
9065                if side.is_some() {
9066                    // We already consumed a side token, need to backtrack that too
9067                    self.current = saved - 1;
9068                }
9069                return (None, None);
9070            }
9071        }
9072
9073        (side, kind)
9074    }
9075
9076    /// Parse CORRESPONDING/STRICT CORRESPONDING/BY NAME modifiers after ALL/DISTINCT
9077    /// Returns (by_name, strict, corresponding, on_columns)
9078    fn parse_set_operation_corresponding(&mut self) -> Result<(bool, bool, bool, Vec<Expression>)> {
9079        let mut by_name = false;
9080        let mut strict = false;
9081        let mut corresponding = false;
9082        let mut on_columns = Vec::new();
9083
9084        // Check for BY NAME (DuckDB style)
9085        if self.match_token(TokenType::By) && self.match_identifier("NAME") {
9086            by_name = true;
9087        }
9088        // Check for STRICT CORRESPONDING (BigQuery style)
9089        else if self.match_identifier("STRICT") {
9090            if self.match_identifier("CORRESPONDING") {
9091                strict = true;
9092                corresponding = true;
9093            } else {
9094                // STRICT without CORRESPONDING - backtrack
9095                self.current -= 1;
9096            }
9097        }
9098        // Check for CORRESPONDING (BigQuery style)
9099        else if self.match_identifier("CORRESPONDING") {
9100            corresponding = true;
9101        }
9102
9103        // If CORRESPONDING is set, check for BY (columns)
9104        if corresponding && self.match_token(TokenType::By) {
9105            self.expect(TokenType::LParen)?;
9106            on_columns = self
9107                .parse_identifier_list()?
9108                .into_iter()
9109                .map(|id| {
9110                    Expression::boxed_column(Column {
9111                        name: id,
9112                        table: None,
9113                        join_mark: false,
9114                        trailing_comments: Vec::new(),
9115                        span: None,
9116                        inferred_type: None,
9117                    })
9118                })
9119                .collect();
9120            self.expect(TokenType::RParen)?;
9121        }
9122
9123        Ok((by_name, strict, corresponding, on_columns))
9124    }
9125
9126    /// Parse ORDER BY, LIMIT, OFFSET modifiers for set operations
9127    fn parse_set_operation_modifiers(&mut self, expr: &mut Expression) -> Result<()> {
9128        // Parse ORDER BY
9129        let order_by = if self.match_token(TokenType::Order) {
9130            self.expect(TokenType::By)?;
9131            Some(self.parse_order_by()?)
9132        } else {
9133            None
9134        };
9135
9136        // Parse LIMIT
9137        let limit = if self.match_token(TokenType::Limit) {
9138            Some(Box::new(self.parse_expression()?))
9139        } else {
9140            None
9141        };
9142
9143        // Parse OFFSET
9144        let offset = if self.match_token(TokenType::Offset) {
9145            Some(Box::new(self.parse_expression()?))
9146        } else {
9147            None
9148        };
9149
9150        // Apply modifiers to the outermost set operation
9151        match expr {
9152            Expression::Union(ref mut union) => {
9153                if order_by.is_some() {
9154                    union.order_by = order_by;
9155                }
9156                if limit.is_some() {
9157                    union.limit = limit;
9158                }
9159                if offset.is_some() {
9160                    union.offset = offset;
9161                }
9162            }
9163            Expression::Intersect(ref mut intersect) => {
9164                if order_by.is_some() {
9165                    intersect.order_by = order_by;
9166                }
9167                if limit.is_some() {
9168                    intersect.limit = limit;
9169                }
9170                if offset.is_some() {
9171                    intersect.offset = offset;
9172                }
9173            }
9174            Expression::Except(ref mut except) => {
9175                if order_by.is_some() {
9176                    except.order_by = order_by;
9177                }
9178                if limit.is_some() {
9179                    except.limit = limit;
9180                }
9181                if offset.is_some() {
9182                    except.offset = offset;
9183                }
9184            }
9185            _ => {}
9186        }
9187        Ok(())
9188    }
9189
9190    /// Parse either a SELECT statement or a parenthesized SELECT/set operation
9191    fn parse_select_or_paren_select(&mut self) -> Result<Expression> {
9192        if self.match_token(TokenType::LParen) {
9193            // Could be (SELECT ...) or ((SELECT ...) UNION ...) or (FROM ...) for DuckDB
9194            if self.check(TokenType::Select)
9195                || self.check(TokenType::With)
9196                || self.check(TokenType::From)
9197            {
9198                let query = self.parse_statement()?;
9199                self.expect(TokenType::RParen)?;
9200                // Handle optional alias after subquery: (SELECT 1) AS a
9201                let alias = if self.match_token(TokenType::As) {
9202                    Some(Identifier::new(self.expect_identifier()?))
9203                } else {
9204                    None
9205                };
9206                // Wrap in Subquery to preserve parentheses
9207                Ok(Expression::Subquery(Box::new(Subquery {
9208                    this: query,
9209                    alias,
9210                    column_aliases: Vec::new(),
9211                    order_by: None,
9212                    limit: None,
9213                    offset: None,
9214                    lateral: false,
9215                    modifiers_inside: false,
9216                    trailing_comments: Vec::new(),
9217                    distribute_by: None,
9218                    sort_by: None,
9219                    cluster_by: None,
9220                    inferred_type: None,
9221                })))
9222            } else if self.check(TokenType::LParen) {
9223                // Nested parentheses like ((SELECT ...))
9224                let inner = self.parse_select_or_paren_select()?;
9225                // Check for set operations inside the parens
9226                let result = self.parse_set_operation(inner)?;
9227                self.expect(TokenType::RParen)?;
9228                // Handle optional alias after subquery
9229                let alias = if self.match_token(TokenType::As) {
9230                    Some(Identifier::new(self.expect_identifier()?))
9231                } else {
9232                    None
9233                };
9234                // Wrap in Subquery to preserve parentheses
9235                Ok(Expression::Subquery(Box::new(Subquery {
9236                    this: result,
9237                    alias,
9238                    column_aliases: Vec::new(),
9239                    order_by: None,
9240                    limit: None,
9241                    offset: None,
9242                    lateral: false,
9243                    modifiers_inside: false,
9244                    trailing_comments: Vec::new(),
9245                    distribute_by: None,
9246                    sort_by: None,
9247                    cluster_by: None,
9248                    inferred_type: None,
9249                })))
9250            } else {
9251                Err(self.parse_error("Expected SELECT or ( after ("))
9252            }
9253        } else if self.check(TokenType::From) {
9254            // DuckDB FROM-first syntax without parentheses: ... UNION FROM t
9255            self.parse_from_first_query()
9256        } else if self.check(TokenType::With) {
9257            // WITH CTE as right-hand side of UNION/INTERSECT/EXCEPT
9258            self.parse_statement()
9259        } else {
9260            // Use parse_select_body (not parse_select) to avoid mutual recursion:
9261            // parse_select calls parse_set_operation, which calls back here.
9262            // The caller (parse_set_operation's loop) handles set-op chaining.
9263            self.parse_select_body()
9264        }
9265    }
9266
9267    /// Parse INSERT statement
9268    fn parse_insert(&mut self) -> Result<Expression> {
9269        let insert_token = self.expect(TokenType::Insert)?;
9270        let leading_comments = insert_token.comments;
9271
9272        // Parse query hint /*+ ... */ if present (Oracle: INSERT /*+ APPEND */ INTO ...)
9273        let hint = if self.check(TokenType::Hint) {
9274            Some(self.parse_hint()?)
9275        } else {
9276            None
9277        };
9278
9279        // Handle SQLite conflict action: INSERT OR ABORT|FAIL|IGNORE|REPLACE|ROLLBACK INTO
9280        let conflict_action = if self.match_token(TokenType::Or) {
9281            if self.match_identifier("ABORT") {
9282                Some("ABORT".to_string())
9283            } else if self.match_identifier("FAIL") {
9284                Some("FAIL".to_string())
9285            } else if self.match_token(TokenType::Ignore) {
9286                Some("IGNORE".to_string())
9287            } else if self.match_token(TokenType::Replace) {
9288                Some("REPLACE".to_string())
9289            } else if self.match_token(TokenType::Rollback) {
9290                Some("ROLLBACK".to_string())
9291            } else {
9292                return Err(self.parse_error(
9293                    "Expected ABORT, FAIL, IGNORE, REPLACE, or ROLLBACK after INSERT OR",
9294                ));
9295            }
9296        } else {
9297            None
9298        };
9299
9300        // Handle INSERT IGNORE (MySQL)
9301        let ignore = conflict_action.is_none() && self.match_token(TokenType::Ignore);
9302
9303        // Handle OVERWRITE for Hive/Spark: INSERT OVERWRITE TABLE ...
9304        let overwrite = self.match_token(TokenType::Overwrite);
9305
9306        // Handle Oracle multi-table INSERT: INSERT ALL/FIRST ...
9307        // Must check before OVERWRITE handling since these are mutually exclusive
9308        if !overwrite && (self.match_token(TokenType::All) || self.match_token(TokenType::First)) {
9309            if let Some(multi_insert) = self.parse_multitable_inserts(leading_comments.clone())? {
9310                return Ok(multi_insert);
9311            }
9312        }
9313
9314        // Handle INTO or TABLE (OVERWRITE requires TABLE, INTO is standard)
9315        // Also handle INSERT OVERWRITE [LOCAL] DIRECTORY 'path'
9316        let local_directory = overwrite && self.match_token(TokenType::Local);
9317        let is_directory = (overwrite || local_directory) && self.match_identifier("DIRECTORY");
9318
9319        if is_directory {
9320            // INSERT OVERWRITE [LOCAL] DIRECTORY 'path' [ROW FORMAT ...] SELECT ...
9321            let path = self.expect_string()?;
9322            // Parse optional ROW FORMAT clause
9323            let row_format = if self.match_keywords(&[TokenType::Row, TokenType::Format]) {
9324                // ROW FORMAT DELIMITED ...
9325                let delimited = self.match_identifier("DELIMITED");
9326                let mut fields_terminated_by = None;
9327                let mut collection_items_terminated_by = None;
9328                let mut map_keys_terminated_by = None;
9329                let mut lines_terminated_by = None;
9330                let mut null_defined_as = None;
9331
9332                // Parse the various TERMINATED BY clauses
9333                loop {
9334                    if self.match_identifier("FIELDS") || self.match_identifier("FIELD") {
9335                        self.match_identifier("TERMINATED");
9336                        self.match_token(TokenType::By);
9337                        fields_terminated_by = Some(self.expect_string()?);
9338                    } else if self.match_identifier("COLLECTION") {
9339                        self.match_identifier("ITEMS");
9340                        self.match_identifier("TERMINATED");
9341                        self.match_token(TokenType::By);
9342                        collection_items_terminated_by = Some(self.expect_string()?);
9343                    } else if self.match_identifier("MAP") {
9344                        self.match_identifier("KEYS");
9345                        self.match_identifier("TERMINATED");
9346                        self.match_token(TokenType::By);
9347                        map_keys_terminated_by = Some(self.expect_string()?);
9348                    } else if self.match_identifier("LINES") {
9349                        self.match_identifier("TERMINATED");
9350                        self.match_token(TokenType::By);
9351                        lines_terminated_by = Some(self.expect_string()?);
9352                    } else if self.match_token(TokenType::Null) {
9353                        self.match_identifier("DEFINED");
9354                        self.match_token(TokenType::As);
9355                        null_defined_as = Some(self.expect_string()?);
9356                    } else {
9357                        break;
9358                    }
9359                }
9360
9361                Some(RowFormat {
9362                    delimited,
9363                    fields_terminated_by,
9364                    collection_items_terminated_by,
9365                    map_keys_terminated_by,
9366                    lines_terminated_by,
9367                    null_defined_as,
9368                })
9369            } else {
9370                None
9371            };
9372
9373            // Parse optional STORED AS clause
9374            let stored_as = if self.match_identifier("STORED") {
9375                self.expect(TokenType::As)?;
9376                Some(self.expect_identifier()?)
9377            } else {
9378                None
9379            };
9380
9381            // Parse the SELECT query
9382            let query = self.parse_statement()?;
9383
9384            return Ok(Expression::Insert(Box::new(Insert {
9385                table: TableRef::new(""),
9386                columns: Vec::new(),
9387                values: Vec::new(),
9388                query: Some(query),
9389                overwrite,
9390                partition: Vec::new(),
9391                directory: Some(DirectoryInsert {
9392                    local: local_directory,
9393                    path,
9394                    row_format,
9395                    stored_as,
9396                }),
9397                returning: Vec::new(),
9398                output: None,
9399                on_conflict: None,
9400                leading_comments,
9401                if_exists: false,
9402                with: None,
9403                ignore,
9404                source_alias: None,
9405                alias: None,
9406                alias_explicit_as: false,
9407                default_values: false,
9408                by_name: false,
9409                conflict_action: conflict_action.clone(),
9410                is_replace: false,
9411                replace_where: None,
9412                source: None,
9413                hint: hint.clone(),
9414                function_target: None,
9415                partition_by: None,
9416                settings: Vec::new(),
9417            })));
9418        }
9419
9420        if overwrite {
9421            // OVERWRITE is typically followed by TABLE
9422            self.match_token(TokenType::Table);
9423        } else {
9424            self.expect(TokenType::Into)?;
9425            // Optional TABLE keyword after INTO
9426            self.match_token(TokenType::Table);
9427        }
9428
9429        // ClickHouse: INSERT INTO [TABLE] FUNCTION func_name(args...)
9430        let mut function_target: Option<Box<Expression>> = None;
9431        if self.match_token(TokenType::Function) {
9432            // Parse function call: func_name(args...)
9433            let func_name = self.expect_identifier_or_keyword()?;
9434            self.expect(TokenType::LParen)?;
9435            let args = if self.check(TokenType::RParen) {
9436                Vec::new()
9437            } else {
9438                self.parse_expression_list()?
9439            };
9440            self.expect(TokenType::RParen)?;
9441            function_target = Some(Box::new(Expression::Function(Box::new(Function {
9442                name: func_name,
9443                args,
9444                distinct: false,
9445                trailing_comments: Vec::new(),
9446                use_bracket_syntax: false,
9447                no_parens: false,
9448                quoted: false,
9449                span: None,
9450                inferred_type: None,
9451            }))));
9452        }
9453
9454        let table_name = if function_target.is_some() {
9455            // For FUNCTION targets, use empty table name
9456            Identifier::new(String::new())
9457        } else {
9458            // Allow keywords (like TABLE) as table names in INSERT statements
9459            self.expect_identifier_or_keyword_with_quoted()?
9460        };
9461        // Handle qualified table names like a.b
9462        let table = if self.match_token(TokenType::Dot) {
9463            let schema = table_name;
9464            let name = self.expect_identifier_or_keyword_with_quoted()?;
9465            let trailing_comments = self.previous_trailing_comments().to_vec();
9466            TableRef {
9467                name,
9468                schema: Some(schema),
9469                catalog: None,
9470                alias: None,
9471                alias_explicit_as: false,
9472                column_aliases: Vec::new(),
9473                leading_comments: Vec::new(),
9474                trailing_comments,
9475                when: None,
9476                only: false,
9477                final_: false,
9478                table_sample: None,
9479                hints: Vec::new(),
9480                system_time: None,
9481                partitions: Vec::new(),
9482                identifier_func: None,
9483                changes: None,
9484                version: None,
9485                span: None,
9486            }
9487        } else {
9488            let trailing_comments = self.previous_trailing_comments().to_vec();
9489            TableRef {
9490                name: table_name,
9491                schema: None,
9492                catalog: None,
9493                alias: None,
9494                alias_explicit_as: false,
9495                column_aliases: Vec::new(),
9496                leading_comments: Vec::new(),
9497                when: None,
9498                only: false,
9499                final_: false,
9500                table_sample: None,
9501                hints: Vec::new(),
9502                system_time: None,
9503                trailing_comments,
9504                partitions: Vec::new(),
9505                identifier_func: None,
9506                changes: None,
9507                version: None,
9508                span: None,
9509            }
9510        };
9511
9512        // Optional alias (PostgreSQL: INSERT INTO table AS t(...), Oracle: INSERT INTO table t ...)
9513        let (alias, alias_explicit_as) = if self.match_token(TokenType::As) {
9514            (Some(Identifier::new(self.expect_identifier()?)), true)
9515        } else if self.is_identifier_token()
9516            && !self.check(TokenType::Values)
9517            && !self.check(TokenType::Select)
9518            && !self.check(TokenType::Default)
9519            && !self.check(TokenType::By)
9520            && !self.check(TokenType::Partition)
9521            && !self.check(TokenType::Output)
9522            && !self.check(TokenType::If)
9523            && !self.check(TokenType::Replace)
9524            && !self.check(TokenType::Table)
9525            && !self.check(TokenType::LParen)
9526        {
9527            // Implicit alias without AS (e.g., INSERT INTO dest d VALUES ...)
9528            (Some(Identifier::new(self.expect_identifier()?)), false)
9529        } else {
9530            (None, false)
9531        };
9532
9533        // Optional IF EXISTS (Hive)
9534        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
9535
9536        // Optional REPLACE WHERE clause (Databricks): INSERT INTO a REPLACE WHERE cond VALUES ...
9537        let replace_where =
9538            if self.match_token(TokenType::Replace) && self.match_token(TokenType::Where) {
9539                Some(Box::new(self.parse_or()?))
9540            } else {
9541                None
9542            };
9543
9544        // Optional PARTITION clause
9545        // ClickHouse: PARTITION BY expr (no parens)
9546        // Hive/Spark: PARTITION (col1 = val1, col2)
9547        let mut partition_by_expr: Option<Box<Expression>> = None;
9548        let partition = if self.check(TokenType::Partition) && self.check_next(TokenType::By) {
9549            // ClickHouse PARTITION BY expr
9550            self.skip(); // consume PARTITION
9551            self.skip(); // consume BY
9552            partition_by_expr = Some(Box::new(self.parse_expression()?));
9553            Vec::new()
9554        } else if self.match_token(TokenType::Partition) {
9555            self.expect(TokenType::LParen)?;
9556            let mut parts = Vec::new();
9557            loop {
9558                let col = Identifier::new(self.expect_identifier()?);
9559                let value = if self.match_token(TokenType::Eq) {
9560                    Some(self.parse_expression()?)
9561                } else {
9562                    None
9563                };
9564                parts.push((col, value));
9565                if !self.match_token(TokenType::Comma) {
9566                    break;
9567                }
9568            }
9569            self.expect(TokenType::RParen)?;
9570            parts
9571        } else {
9572            Vec::new()
9573        };
9574
9575        // ClickHouse: SETTINGS key = val, ...
9576        let insert_settings = if self.match_token(TokenType::Settings) {
9577            let mut settings = Vec::new();
9578            loop {
9579                settings.push(self.parse_expression()?);
9580                if !self.match_token(TokenType::Comma) {
9581                    break;
9582                }
9583            }
9584            settings
9585        } else {
9586            Vec::new()
9587        };
9588
9589        // Optional column list OR parenthesized subquery
9590        // We need to check if ( is followed by SELECT/WITH (subquery) or identifiers (column list)
9591        let columns = if self.check(TokenType::LParen) {
9592            // Look ahead to see if this is a subquery or column list
9593            if self
9594                .peek_nth(1)
9595                .map(|t| t.token_type == TokenType::Select || t.token_type == TokenType::With)
9596                .unwrap_or(false)
9597            {
9598                // This is a parenthesized subquery, not a column list
9599                Vec::new()
9600            } else if matches!(
9601                self.config.dialect,
9602                Some(crate::dialects::DialectType::ClickHouse)
9603            ) && {
9604                // ClickHouse: INSERT INTO t (*), t(* EXCEPT ...), t(table.* EXCEPT ...), t(COLUMNS('pattern') EXCEPT ...)
9605                let peek1 = self.peek_nth(1).map(|t| t.token_type);
9606                peek1 == Some(TokenType::Star)
9607                    || (peek1 == Some(TokenType::Var)
9608                        && self.peek_nth(2).map(|t| t.token_type) == Some(TokenType::Dot)
9609                        && self.peek_nth(3).map(|t| t.token_type) == Some(TokenType::Star))
9610                    || (peek1 == Some(TokenType::Var)
9611                        && self
9612                            .peek_nth(1)
9613                            .map(|t| t.text.eq_ignore_ascii_case("COLUMNS"))
9614                            .unwrap_or(false))
9615            } {
9616                // Consume balanced parens and skip entire column specification
9617                self.skip(); // consume (
9618                let mut depth = 1i32;
9619                while !self.is_at_end() && depth > 0 {
9620                    if self.check(TokenType::LParen) {
9621                        depth += 1;
9622                    }
9623                    if self.check(TokenType::RParen) {
9624                        depth -= 1;
9625                        if depth == 0 {
9626                            break;
9627                        }
9628                    }
9629                    self.skip();
9630                }
9631                self.expect(TokenType::RParen)?;
9632                Vec::new() // Treat as "all columns"
9633            } else {
9634                self.skip(); // consume (
9635                let cols = self.parse_identifier_list()?;
9636                self.expect(TokenType::RParen)?;
9637                cols
9638            }
9639        } else {
9640            Vec::new()
9641        };
9642
9643        // Parse OUTPUT clause (TSQL)
9644        let output = if self.match_token(TokenType::Output) {
9645            Some(self.parse_output_clause()?)
9646        } else {
9647            None
9648        };
9649
9650        // Check for BY NAME (DuckDB): INSERT INTO x BY NAME SELECT ...
9651        let by_name = self.match_token(TokenType::By) && self.match_identifier("NAME");
9652
9653        // Check for DEFAULT VALUES (PostgreSQL)
9654        let default_values =
9655            self.match_token(TokenType::Default) && self.match_token(TokenType::Values);
9656
9657        // VALUES or SELECT or TABLE source (Hive/Spark) or DEFAULT VALUES (already consumed above)
9658        let (values, query) = if default_values {
9659            // DEFAULT VALUES: no values or query
9660            (Vec::new(), None)
9661        } else if matches!(
9662            self.config.dialect,
9663            Some(crate::dialects::DialectType::ClickHouse)
9664        ) && self.check(TokenType::Format)
9665            && self.peek_nth(1).is_some_and(|t| {
9666                !t.text.eq_ignore_ascii_case("VALUES")
9667                    && (t.token_type == TokenType::Var || t.token_type == TokenType::Identifier)
9668            })
9669        {
9670            // ClickHouse: FORMAT <format_name> followed by raw data (CSV, JSON, TSV, etc.)
9671            // Skip everything to next semicolon or end — the data is not SQL
9672            self.skip(); // consume FORMAT
9673            let format_name = self.advance().text.clone(); // consume format name
9674                                                           // Consume all remaining tokens until semicolon (raw data)
9675            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
9676                self.skip();
9677            }
9678            // Store as empty values with the format name in the query as a command
9679            (
9680                Vec::new(),
9681                Some(Expression::Command(Box::new(crate::expressions::Command {
9682                    this: format!("FORMAT {}", format_name),
9683                }))),
9684            )
9685        } else if matches!(
9686            self.config.dialect,
9687            Some(crate::dialects::DialectType::ClickHouse)
9688        ) && self.match_text_seq(&["FORMAT", "VALUES"])
9689        {
9690            let mut all_values = Vec::new();
9691
9692            loop {
9693                self.expect(TokenType::LParen)?;
9694                let row = self.parse_expression_list()?;
9695                self.expect(TokenType::RParen)?;
9696                all_values.push(row);
9697
9698                if !self.match_token(TokenType::Comma) {
9699                    break;
9700                }
9701            }
9702
9703            (all_values, None)
9704        } else if self.match_token(TokenType::Values) {
9705            let mut all_values = Vec::new();
9706
9707            // ClickHouse: INSERT INTO t VALUES; — empty VALUES (clientError expected)
9708            if matches!(
9709                self.config.dialect,
9710                Some(crate::dialects::DialectType::ClickHouse)
9711            ) && (self.check(TokenType::Semicolon) || self.is_at_end())
9712            {
9713                // Return empty INSERT as Command to avoid needing all Insert fields
9714                return Ok(Expression::Command(Box::new(crate::expressions::Command {
9715                    this: "INSERT INTO VALUES".to_string(),
9716                })));
9717            }
9718
9719            // ClickHouse: allow bare VALUES without parens: VALUES 1, 2, 3
9720            if matches!(
9721                self.config.dialect,
9722                Some(crate::dialects::DialectType::ClickHouse)
9723            ) && !self.check(TokenType::LParen)
9724            {
9725                loop {
9726                    let val = self.parse_expression()?;
9727                    all_values.push(vec![val]);
9728                    if !self.match_token(TokenType::Comma) {
9729                        break;
9730                    }
9731                }
9732            } else {
9733                loop {
9734                    self.expect(TokenType::LParen)?;
9735                    // ClickHouse: allow empty VALUES () — empty tuple
9736                    let row = if self.check(TokenType::RParen) {
9737                        Vec::new()
9738                    } else {
9739                        self.parse_values_expression_list()?
9740                    };
9741                    self.expect(TokenType::RParen)?;
9742                    all_values.push(row);
9743
9744                    if !self.match_token(TokenType::Comma) {
9745                        // ClickHouse: allow tuples without commas: VALUES (1) (2) (3)
9746                        if matches!(
9747                            self.config.dialect,
9748                            Some(crate::dialects::DialectType::ClickHouse)
9749                        ) && self.check(TokenType::LParen)
9750                        {
9751                            continue;
9752                        }
9753                        break;
9754                    }
9755                    // ClickHouse: allow trailing comma after last tuple
9756                    if matches!(
9757                        self.config.dialect,
9758                        Some(crate::dialects::DialectType::ClickHouse)
9759                    ) && !self.check(TokenType::LParen)
9760                    {
9761                        break;
9762                    }
9763                }
9764            } // close else (parenthesized values)
9765
9766            (all_values, None)
9767        } else if self.check(TokenType::Table) {
9768            // Hive/Spark: INSERT OVERWRITE TABLE target TABLE source
9769            // The TABLE keyword here indicates source table, not a subquery
9770            (Vec::new(), None)
9771        } else {
9772            (Vec::new(), Some(self.parse_statement()?))
9773        };
9774
9775        // Parse source table (Hive/Spark): INSERT OVERWRITE TABLE target TABLE source
9776        let source = if self.match_token(TokenType::Table) {
9777            // Parse source table reference (similar to target table parsing)
9778            let source_name = self.expect_identifier_with_quoted()?;
9779            let source_table = if self.match_token(TokenType::Dot) {
9780                let schema = source_name;
9781                let name = self.expect_identifier_with_quoted()?;
9782                let trailing_comments = self.previous_trailing_comments().to_vec();
9783                TableRef {
9784                    name,
9785                    schema: Some(schema),
9786                    catalog: None,
9787                    alias: None,
9788                    alias_explicit_as: false,
9789                    column_aliases: Vec::new(),
9790                    leading_comments: Vec::new(),
9791                    trailing_comments,
9792                    when: None,
9793                    only: false,
9794                    final_: false,
9795                    table_sample: None,
9796                    hints: Vec::new(),
9797                    system_time: None,
9798                    partitions: Vec::new(),
9799                    identifier_func: None,
9800                    changes: None,
9801                    version: None,
9802                    span: None,
9803                }
9804            } else {
9805                let trailing_comments = self.previous_trailing_comments().to_vec();
9806                TableRef {
9807                    name: source_name,
9808                    schema: None,
9809                    catalog: None,
9810                    alias: None,
9811                    alias_explicit_as: false,
9812                    column_aliases: Vec::new(),
9813                    leading_comments: Vec::new(),
9814                    trailing_comments,
9815                    when: None,
9816                    only: false,
9817                    final_: false,
9818                    table_sample: None,
9819                    hints: Vec::new(),
9820                    system_time: None,
9821                    partitions: Vec::new(),
9822                    identifier_func: None,
9823                    changes: None,
9824                    version: None,
9825                    span: None,
9826                }
9827            };
9828            Some(Expression::Table(Box::new(source_table)))
9829        } else {
9830            None
9831        };
9832
9833        // Parse optional AS alias after VALUES (MySQL: INSERT ... VALUES (...) AS new_data)
9834        let source_alias = if self.match_token(TokenType::As) {
9835            Some(Identifier::new(self.expect_identifier()?))
9836        } else {
9837            None
9838        };
9839
9840        // Parse ON CONFLICT clause (PostgreSQL, SQLite) or ON DUPLICATE KEY UPDATE (MySQL)
9841        let on_conflict = if self.match_token(TokenType::On) {
9842            if self.match_identifier("CONFLICT") {
9843                Some(Box::new(self.parse_on_conflict()?))
9844            } else if self.match_identifier("DUPLICATE") {
9845                // MySQL: ON DUPLICATE KEY UPDATE
9846                self.expect(TokenType::Key)?;
9847                self.expect(TokenType::Update)?;
9848
9849                // Parse the UPDATE SET expressions
9850                let mut sets = Vec::new();
9851                loop {
9852                    // Parse column = expression
9853                    let col_name = self.expect_identifier_with_quoted()?;
9854                    // Handle qualified column: table.column
9855                    let column = if self.match_token(TokenType::Dot) {
9856                        let col = self.expect_identifier_with_quoted()?;
9857                        Expression::boxed_column(Column {
9858                            name: col,
9859                            table: Some(col_name),
9860                            join_mark: false,
9861                            trailing_comments: Vec::new(),
9862                            span: None,
9863                            inferred_type: None,
9864                        })
9865                    } else {
9866                        Expression::Identifier(col_name)
9867                    };
9868                    self.expect(TokenType::Eq)?;
9869                    let value = self.parse_expression()?;
9870                    sets.push(Expression::Eq(Box::new(BinaryOp {
9871                        left: column,
9872                        right: value,
9873                        left_comments: Vec::new(),
9874                        operator_comments: Vec::new(),
9875                        trailing_comments: Vec::new(),
9876                        inferred_type: None,
9877                    })));
9878                    if !self.match_token(TokenType::Comma) {
9879                        break;
9880                    }
9881                }
9882
9883                Some(Box::new(Expression::OnConflict(Box::new(OnConflict {
9884                    duplicate: Some(Box::new(Expression::Boolean(BooleanLiteral {
9885                        value: true,
9886                    }))),
9887                    expressions: sets,
9888                    action: None,
9889                    conflict_keys: None,
9890                    index_predicate: None,
9891                    constraint: None,
9892                    where_: None,
9893                }))))
9894            } else {
9895                // Unexpected token after ON
9896                return Err(self.parse_error("Expected CONFLICT or DUPLICATE after ON"));
9897            }
9898        } else {
9899            None
9900        };
9901
9902        // Parse RETURNING clause (PostgreSQL, SQLite)
9903        let returning = if self.match_token(TokenType::Returning) {
9904            self.parse_select_expressions()?
9905        } else {
9906            Vec::new()
9907        };
9908
9909        Ok(Expression::Insert(Box::new(Insert {
9910            table,
9911            columns,
9912            values,
9913            query,
9914            overwrite,
9915            partition,
9916            directory: None,
9917            returning,
9918            output,
9919            on_conflict,
9920            leading_comments,
9921            if_exists,
9922            with: None,
9923            ignore,
9924            source_alias,
9925            alias,
9926            alias_explicit_as,
9927            default_values,
9928            by_name,
9929            conflict_action,
9930            is_replace: false,
9931            replace_where,
9932            source: source.map(Box::new),
9933            hint,
9934            function_target,
9935            partition_by: partition_by_expr,
9936            settings: insert_settings,
9937        })))
9938    }
9939
9940    /// Parse ON CONFLICT clause for INSERT statements (PostgreSQL, SQLite)
9941    /// Syntax: ON CONFLICT [(conflict_target)] [WHERE predicate] DO NOTHING | DO UPDATE SET ...
9942    /// ON CONFLICT ON CONSTRAINT constraint_name DO ...
9943    fn parse_on_conflict(&mut self) -> Result<Expression> {
9944        // Check for ON CONSTRAINT variant
9945        let constraint =
9946            if self.match_token(TokenType::On) && self.match_token(TokenType::Constraint) {
9947                let name = self.expect_identifier()?;
9948                Some(Box::new(Expression::Identifier(Identifier::new(name))))
9949            } else {
9950                None
9951            };
9952
9953        // Parse optional conflict target (column list)
9954        let conflict_keys = if constraint.is_none() && self.match_token(TokenType::LParen) {
9955            let keys = self.parse_expression_list()?;
9956            self.expect(TokenType::RParen)?;
9957            Some(Box::new(Expression::Tuple(Box::new(Tuple {
9958                expressions: keys,
9959            }))))
9960        } else {
9961            None
9962        };
9963
9964        // Parse optional WHERE clause for conflict target
9965        let index_predicate = if self.match_token(TokenType::Where) {
9966            Some(Box::new(self.parse_expression()?))
9967        } else {
9968            None
9969        };
9970
9971        // Parse DO NOTHING or DO UPDATE
9972        if !self.match_identifier("DO") {
9973            return Err(self.parse_error("Expected DO after ON CONFLICT"));
9974        }
9975
9976        let action = if self.match_identifier("NOTHING") {
9977            // DO NOTHING
9978            Some(Box::new(Expression::Identifier(Identifier::new(
9979                "NOTHING".to_string(),
9980            ))))
9981        } else if self.match_token(TokenType::Update) {
9982            // DO UPDATE SET ...
9983            self.expect(TokenType::Set)?;
9984            let mut sets = Vec::new();
9985            loop {
9986                // Parse column = expression
9987                let col_name = self.expect_identifier_with_quoted()?;
9988                // Handle qualified column: table.column
9989                let column = if self.match_token(TokenType::Dot) {
9990                    let col = self.expect_identifier_with_quoted()?;
9991                    Expression::boxed_column(Column {
9992                        name: col,
9993                        table: Some(col_name),
9994                        join_mark: false,
9995                        trailing_comments: Vec::new(),
9996                        span: None,
9997                        inferred_type: None,
9998                    })
9999                } else {
10000                    Expression::Identifier(col_name)
10001                };
10002                self.expect(TokenType::Eq)?;
10003                let value = self.parse_expression()?;
10004                sets.push(Expression::Eq(Box::new(BinaryOp {
10005                    left: column,
10006                    right: value,
10007                    left_comments: Vec::new(),
10008                    operator_comments: Vec::new(),
10009                    trailing_comments: Vec::new(),
10010                    inferred_type: None,
10011                })));
10012                if !self.match_token(TokenType::Comma) {
10013                    break;
10014                }
10015            }
10016            Some(Box::new(Expression::Tuple(Box::new(Tuple {
10017                expressions: sets,
10018            }))))
10019        } else {
10020            return Err(self.parse_error("Expected NOTHING or UPDATE after DO"));
10021        };
10022
10023        // Parse optional WHERE clause for the UPDATE action
10024        let where_ = if self.match_token(TokenType::Where) {
10025            Some(Box::new(self.parse_expression()?))
10026        } else {
10027            None
10028        };
10029
10030        Ok(Expression::OnConflict(Box::new(OnConflict {
10031            duplicate: None,
10032            expressions: Vec::new(),
10033            action,
10034            conflict_keys,
10035            index_predicate,
10036            constraint,
10037            where_,
10038        })))
10039    }
10040
10041    /// Parse MySQL REPLACE [INTO] statement or REPLACE() function call
10042    fn parse_replace(&mut self) -> Result<Expression> {
10043        // Check if this is REPLACE() function call (REPLACE followed by '(')
10044        // or MySQL REPLACE INTO statement
10045        let replace_token = self.expect(TokenType::Replace)?;
10046        let leading_comments = replace_token.comments;
10047
10048        if self.check(TokenType::LParen) {
10049            // This is a REPLACE() function call, parse as expression
10050            self.expect(TokenType::LParen)?;
10051            let args = self.parse_expression_list()?;
10052            self.expect(TokenType::RParen)?;
10053            return Ok(Expression::Function(Box::new(Function {
10054                name: "REPLACE".to_string(),
10055                args,
10056                distinct: false,
10057                trailing_comments: Vec::new(),
10058                use_bracket_syntax: false,
10059                no_parens: false,
10060                quoted: false,
10061                span: None,
10062                inferred_type: None,
10063            })));
10064        }
10065
10066        // Teradata: REPLACE VIEW -> CREATE OR REPLACE VIEW
10067        if matches!(
10068            self.config.dialect,
10069            Some(crate::dialects::DialectType::Teradata)
10070        ) && self.check(TokenType::View)
10071        {
10072            return self.parse_create_view(true, false, false, false, None, None, None, false);
10073        }
10074
10075        // ClickHouse: REPLACE TABLE -> treat like CREATE OR REPLACE TABLE
10076        // Also handle REPLACE TEMPORARY TABLE
10077        if matches!(
10078            self.config.dialect,
10079            Some(crate::dialects::DialectType::ClickHouse)
10080        ) && (self.check(TokenType::Table) || self.check(TokenType::Temporary))
10081        {
10082            let temporary = self.match_token(TokenType::Temporary);
10083            return self.parse_create_table(true, temporary, leading_comments.clone(), None);
10084        }
10085
10086        // ClickHouse: REPLACE DICTIONARY -> consume as Command
10087        if matches!(
10088            self.config.dialect,
10089            Some(crate::dialects::DialectType::ClickHouse)
10090        ) && (self.check(TokenType::Dictionary) || self.check_identifier("DICTIONARY"))
10091        {
10092            let mut parts = vec!["REPLACE".to_string()];
10093            let mut _paren_depth = 0i32;
10094            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
10095                let token = self.advance();
10096                if token.token_type == TokenType::LParen {
10097                    _paren_depth += 1;
10098                }
10099                if token.token_type == TokenType::RParen {
10100                    _paren_depth -= 1;
10101                }
10102                let text = if token.token_type == TokenType::String {
10103                    format!("'{}'", token.text)
10104                } else if token.token_type == TokenType::QuotedIdentifier {
10105                    format!("\"{}\"", token.text)
10106                } else {
10107                    token.text.clone()
10108                };
10109                parts.push(text);
10110            }
10111            return Ok(Expression::Command(Box::new(crate::expressions::Command {
10112                this: parts.join(" "),
10113            })));
10114        }
10115
10116        // Otherwise, this is MySQL/SQLite REPLACE INTO statement - parse similarly to INSERT
10117        self.match_token(TokenType::Into);
10118
10119        let table_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
10120        let table = if self.match_token(TokenType::Dot) {
10121            let second_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
10122            TableRef {
10123                name: second_name,
10124                schema: Some(table_name),
10125                catalog: None,
10126                alias: None,
10127                alias_explicit_as: false,
10128                column_aliases: Vec::new(),
10129                leading_comments: Vec::new(),
10130                trailing_comments: Vec::new(),
10131                when: None,
10132                only: false,
10133                final_: false,
10134                table_sample: None,
10135                hints: Vec::new(),
10136                system_time: None,
10137                partitions: Vec::new(),
10138                identifier_func: None,
10139                changes: None,
10140                version: None,
10141                span: None,
10142            }
10143        } else {
10144            TableRef::new(table_name.name)
10145        };
10146
10147        // Parse optional column list
10148        let columns = if self.match_token(TokenType::LParen) {
10149            let mut cols = Vec::new();
10150            loop {
10151                if self.check(TokenType::RParen) {
10152                    break;
10153                }
10154                let col = self.expect_identifier_with_quoted()?;
10155                cols.push(col);
10156                if !self.match_token(TokenType::Comma) {
10157                    break;
10158                }
10159            }
10160            self.expect(TokenType::RParen)?;
10161            cols
10162        } else {
10163            Vec::new()
10164        };
10165
10166        // Parse VALUES or SELECT query
10167        let mut values = Vec::new();
10168        let query = if self.match_token(TokenType::Values) {
10169            loop {
10170                self.expect(TokenType::LParen)?;
10171                let row = self.parse_expression_list()?;
10172                self.expect(TokenType::RParen)?;
10173                values.push(row);
10174                if !self.match_token(TokenType::Comma) {
10175                    break;
10176                }
10177            }
10178            None
10179        } else if !self.is_at_end() && !self.check(TokenType::Semicolon) {
10180            // SELECT or other statement as value source
10181            Some(self.parse_statement()?)
10182        } else {
10183            None
10184        };
10185
10186        Ok(Expression::Insert(Box::new(Insert {
10187            table,
10188            columns,
10189            values,
10190            query,
10191            overwrite: false,
10192            partition: Vec::new(),
10193            directory: None,
10194            returning: Vec::new(),
10195            output: None,
10196            on_conflict: None,
10197            leading_comments,
10198            if_exists: false,
10199            with: None,
10200            ignore: false,
10201            source_alias: None,
10202            alias: None,
10203            alias_explicit_as: false,
10204            default_values: false,
10205            by_name: false,
10206            conflict_action: None,
10207            is_replace: true,
10208            replace_where: None,
10209            source: None,
10210            hint: None,
10211            function_target: None,
10212            partition_by: None,
10213            settings: Vec::new(),
10214        })))
10215    }
10216
10217    /// Parse UPDATE statement
10218    fn parse_update(&mut self) -> Result<Expression> {
10219        let update_token = self.expect(TokenType::Update)?;
10220        let leading_comments = update_token.comments;
10221
10222        // TSQL: UPDATE STATISTICS table_name - parse as Command
10223        if self.check_identifier("STATISTICS") {
10224            let mut parts = vec!["UPDATE".to_string()];
10225            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
10226                parts.push(self.advance().text);
10227            }
10228            return Ok(Expression::Command(Box::new(Command {
10229                this: parts.join(" "),
10230            })));
10231        }
10232
10233        // PostgreSQL ONLY modifier: UPDATE ONLY t SET ...
10234        let has_only = self.match_token(TokenType::Only);
10235
10236        // Parse table name (can be qualified: db.table_name)
10237        let first_name = self.expect_identifier_with_quoted()?;
10238        let mut table = if self.match_token(TokenType::Dot) {
10239            let second_name = self.expect_identifier_with_quoted()?;
10240            // Check for three-part name (catalog.schema.table)
10241            if self.match_token(TokenType::Dot) {
10242                let table_name = self.expect_identifier_with_quoted()?;
10243                TableRef {
10244                    name: table_name,
10245                    schema: Some(second_name),
10246                    catalog: Some(first_name),
10247                    alias: None,
10248                    alias_explicit_as: false,
10249                    column_aliases: Vec::new(),
10250                    leading_comments: Vec::new(),
10251                    trailing_comments: Vec::new(),
10252                    when: None,
10253                    only: false,
10254                    final_: false,
10255                    table_sample: None,
10256                    hints: Vec::new(),
10257                    system_time: None,
10258                    partitions: Vec::new(),
10259                    identifier_func: None,
10260                    changes: None,
10261                    version: None,
10262                    span: None,
10263                }
10264            } else {
10265                TableRef {
10266                    name: second_name,
10267                    schema: Some(first_name),
10268                    catalog: None,
10269                    alias: None,
10270                    alias_explicit_as: false,
10271                    column_aliases: Vec::new(),
10272                    leading_comments: Vec::new(),
10273                    trailing_comments: Vec::new(),
10274                    when: None,
10275                    only: false,
10276                    final_: false,
10277                    table_sample: None,
10278                    hints: Vec::new(),
10279                    system_time: None,
10280                    partitions: Vec::new(),
10281                    identifier_func: None,
10282                    changes: None,
10283                    version: None,
10284                    span: None,
10285                }
10286            }
10287        } else {
10288            TableRef::from_identifier(first_name)
10289        };
10290        table.trailing_comments = self.previous_trailing_comments().to_vec();
10291        if has_only {
10292            table.only = true;
10293        }
10294
10295        // Optional alias (with or without AS)
10296        if self.match_token(TokenType::As) {
10297            table.alias = Some(self.expect_identifier_with_quoted()?);
10298            table.alias_explicit_as = true;
10299        } else if self.is_identifier_token() && !self.check(TokenType::Set) {
10300            // Implicit alias (table t SET ...)
10301            table.alias = Some(self.expect_identifier_with_quoted()?);
10302            table.alias_explicit_as = false;
10303        }
10304
10305        // Handle multi-table UPDATE syntax: UPDATE t1, t2, t3 LEFT JOIN t4 ON ... SET ...
10306        // Capture additional tables
10307        let mut extra_tables = Vec::new();
10308        while self.match_token(TokenType::Comma) {
10309            // Parse additional table name
10310            let first_name = self.expect_identifier_with_quoted()?;
10311            let mut extra_table = if self.match_token(TokenType::Dot) {
10312                let second_name = self.expect_identifier_with_quoted()?;
10313                if self.match_token(TokenType::Dot) {
10314                    let table_name = self.expect_identifier_with_quoted()?;
10315                    TableRef {
10316                        name: table_name,
10317                        schema: Some(second_name),
10318                        catalog: Some(first_name),
10319                        alias: None,
10320                        alias_explicit_as: false,
10321                        column_aliases: Vec::new(),
10322                        leading_comments: Vec::new(),
10323                        trailing_comments: Vec::new(),
10324                        when: None,
10325                        only: false,
10326                        final_: false,
10327                        table_sample: None,
10328                        hints: Vec::new(),
10329                        system_time: None,
10330                        partitions: Vec::new(),
10331                        identifier_func: None,
10332                        changes: None,
10333                        version: None,
10334                        span: None,
10335                    }
10336                } else {
10337                    TableRef {
10338                        name: second_name,
10339                        schema: Some(first_name),
10340                        catalog: None,
10341                        alias: None,
10342                        alias_explicit_as: false,
10343                        column_aliases: Vec::new(),
10344                        leading_comments: Vec::new(),
10345                        trailing_comments: Vec::new(),
10346                        when: None,
10347                        only: false,
10348                        final_: false,
10349                        table_sample: None,
10350                        hints: Vec::new(),
10351                        system_time: None,
10352                        partitions: Vec::new(),
10353                        identifier_func: None,
10354                        changes: None,
10355                        version: None,
10356                        span: None,
10357                    }
10358                }
10359            } else {
10360                TableRef::from_identifier(first_name)
10361            };
10362            // Optional alias
10363            if self.match_token(TokenType::As) {
10364                extra_table.alias = Some(self.expect_identifier_with_quoted()?);
10365                extra_table.alias_explicit_as = true;
10366            } else if self.is_identifier_token()
10367                && !self.check(TokenType::Set)
10368                && !self.check_keyword()
10369            {
10370                extra_table.alias = Some(self.expect_identifier_with_quoted()?);
10371                extra_table.alias_explicit_as = false;
10372            }
10373            extra_tables.push(extra_table);
10374        }
10375
10376        // Handle JOINs before SET
10377        let mut table_joins = Vec::new();
10378        while let Some((kind, _, use_inner_keyword, use_outer_keyword, _join_hint)) =
10379            self.try_parse_join_kind()
10380        {
10381            if self.check(TokenType::Join) {
10382                self.skip(); // consume JOIN
10383            }
10384            // Parse joined table (supports subqueries, LATERAL, functions, etc.)
10385            let join_expr = self.parse_table_expression()?;
10386            // ON clause
10387            let on_condition = if self.match_token(TokenType::On) {
10388                Some(self.parse_expression()?)
10389            } else {
10390                None
10391            };
10392            table_joins.push(Join {
10393                this: join_expr,
10394                on: on_condition,
10395                using: Vec::new(),
10396                kind,
10397                use_inner_keyword,
10398                use_outer_keyword,
10399                deferred_condition: false,
10400                join_hint: None,
10401                match_condition: None,
10402                pivots: Vec::new(),
10403                comments: Vec::new(),
10404                nesting_group: 0,
10405                directed: false,
10406            });
10407        }
10408
10409        // Snowflake syntax: UPDATE table FROM (source) SET ... WHERE ...
10410        // Check if FROM comes before SET
10411        let (from_before_set, early_from_clause, early_from_joins) =
10412            if self.match_token(TokenType::From) {
10413                let from_clause = self.parse_from()?;
10414                let from_joins = self.parse_joins()?;
10415                (true, Some(from_clause), from_joins)
10416            } else {
10417                (false, None, Vec::new())
10418            };
10419
10420        self.expect(TokenType::Set)?;
10421
10422        let mut set = Vec::new();
10423        loop {
10424            // Column can be qualified for multi-table UPDATE (e.g., a.id = 1)
10425            // Use safe keyword variant to allow keywords like 'exists' as column names (ClickHouse)
10426            let mut col_ident = self.expect_identifier_or_safe_keyword_with_quoted()?;
10427            while self.match_token(TokenType::Dot) {
10428                let part = self.expect_identifier_or_safe_keyword_with_quoted()?;
10429                // For qualified columns, preserve both parts
10430                col_ident = Identifier {
10431                    name: format!("{}.{}", col_ident.name, part.name),
10432                    quoted: col_ident.quoted || part.quoted,
10433                    trailing_comments: Vec::new(),
10434                    span: None,
10435                };
10436            }
10437            self.expect(TokenType::Eq)?;
10438            let value = self.parse_expression()?;
10439            set.push((col_ident, value));
10440
10441            if !self.match_token(TokenType::Comma) {
10442                break;
10443            }
10444        }
10445
10446        // Parse OUTPUT clause (TSQL)
10447        let output = if self.match_token(TokenType::Output) {
10448            Some(self.parse_output_clause()?)
10449        } else {
10450            None
10451        };
10452
10453        // Parse FROM clause (PostgreSQL, SQL Server, Snowflake) - only if not already parsed before SET
10454        let (from_clause, from_joins) = if from_before_set {
10455            (early_from_clause, early_from_joins)
10456        } else if self.match_token(TokenType::From) {
10457            let from_clause = Some(self.parse_from()?);
10458            let from_joins = self.parse_joins()?;
10459            (from_clause, from_joins)
10460        } else {
10461            (None, Vec::new())
10462        };
10463
10464        let where_clause = if self.match_token(TokenType::Where) {
10465            Some(Where {
10466                this: self.parse_expression()?,
10467            })
10468        } else {
10469            None
10470        };
10471
10472        // Parse RETURNING clause (PostgreSQL, SQLite)
10473        let returning = if self.match_token(TokenType::Returning) {
10474            self.parse_select_expressions()?
10475        } else {
10476            Vec::new()
10477        };
10478
10479        // Parse ORDER BY clause (MySQL)
10480        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
10481            Some(self.parse_order_by()?)
10482        } else {
10483            None
10484        };
10485
10486        // Parse LIMIT clause (MySQL)
10487        let limit = if self.match_token(TokenType::Limit) {
10488            Some(self.parse_expression()?)
10489        } else {
10490            None
10491        };
10492
10493        Ok(Expression::Update(Box::new(Update {
10494            table,
10495            extra_tables,
10496            table_joins,
10497            set,
10498            from_clause,
10499            from_joins,
10500            where_clause,
10501            returning,
10502            output,
10503            with: None,
10504            leading_comments,
10505            limit,
10506            order_by,
10507            from_before_set,
10508        })))
10509    }
10510
10511    /// Parse DELETE statement
10512    /// Handles:
10513    /// - Standard: DELETE FROM t WHERE ...
10514    /// - PostgreSQL USING: DELETE FROM t USING s WHERE ... RETURNING a
10515    /// - DuckDB USING: DELETE FROM t USING (VALUES ...) AS t1 WHERE ...
10516    /// - MySQL multi-table: DELETE t1 FROM t1 JOIN t2 ON ... WHERE ...
10517    /// - MySQL multi-table: DELETE t1, t2 FROM t1 JOIN t2 JOIN t3 WHERE ...
10518    /// - MySQL USING: DELETE FROM t1, t2 USING t1 JOIN t2 JOIN t3 WHERE ...
10519    /// - MySQL FORCE INDEX: DELETE FROM t FORCE INDEX (idx) WHERE ...
10520    fn parse_delete(&mut self) -> Result<Expression> {
10521        let delete_token = self.expect(TokenType::Delete)?;
10522        let leading_comments = delete_token.comments;
10523
10524        // Check if FROM is present. If not, this is MySQL multi-table: DELETE t1, t2 FROM ...
10525        // or TSQL: DELETE x OUTPUT x.a FROM z
10526        let mut tables = Vec::new();
10527        let mut early_output = None;
10528        let _has_from = if self.check(TokenType::From) {
10529            self.skip(); // consume FROM
10530            true
10531        } else {
10532            // MySQL multi-table: DELETE t1[, t2, ...] FROM ...
10533            // or TSQL: DELETE x OUTPUT x.a FROM z
10534            // or BigQuery/generic: DELETE table WHERE ... (no FROM required)
10535            // Parse target table list (supporting dotted names)
10536            loop {
10537                let tref = self.parse_table_ref()?;
10538                tables.push(tref);
10539                if !self.match_token(TokenType::Comma) {
10540                    break;
10541                }
10542            }
10543            // TSQL: OUTPUT clause can appear before FROM
10544            if self.match_token(TokenType::Output) {
10545                early_output = Some(self.parse_output_clause()?);
10546            }
10547            if self.check(TokenType::From) {
10548                self.skip(); // consume FROM
10549                true
10550            } else {
10551                // BigQuery-style: DELETE table WHERE ... (no FROM)
10552                false
10553            }
10554        };
10555
10556        // Now parse the main table after FROM (or use from no-FROM path)
10557        let has_only = self.match_token(TokenType::Only);
10558        let mut table = if _has_from {
10559            // Parse the main table(s) after FROM
10560            // Use parse_table_ref() to handle dotted names like db.table
10561            self.parse_table_ref()?
10562        } else {
10563            // BigQuery-style: table was already parsed into `tables`
10564            // Move it out to be the main table
10565            if !tables.is_empty() {
10566                tables.remove(0)
10567            } else {
10568                return Err(self.parse_error("Expected table name in DELETE statement"));
10569            }
10570        };
10571        if has_only {
10572            table.only = true;
10573        }
10574
10575        // ClickHouse: ON CLUSTER clause
10576        let on_cluster = self.parse_on_cluster_clause()?;
10577
10578        // Check for additional tables after the first: DELETE FROM t1, t2 USING ...
10579        let mut extra_from_tables = Vec::new();
10580        if _has_from
10581            && tables.is_empty()
10582            && self.check(TokenType::Comma)
10583            && !self.check(TokenType::Where)
10584        {
10585            // Could be multi-table: DELETE FROM t1, t2 USING ...
10586            // Check ahead if this is followed by USING or more tables
10587            while self.match_token(TokenType::Comma) {
10588                let extra_name = self.expect_identifier_with_quoted()?;
10589                let extra_ref = TableRef::from_identifier(extra_name);
10590                extra_from_tables.push(extra_ref);
10591            }
10592        }
10593
10594        // If we had DELETE FROM t1, t2 USING ..., the tables field stores t1, t2
10595        let mut tables_from_using = false;
10596        if !extra_from_tables.is_empty() {
10597            // The main table + extra tables form the multi-table target
10598            tables.push(table.clone());
10599            tables.append(&mut extra_from_tables);
10600            tables_from_using = true;
10601        }
10602
10603        // Check for FORCE INDEX hint (MySQL): DELETE FROM t FORCE INDEX (idx)
10604        let force_index = if self.match_text_seq(&["FORCE", "INDEX"]) {
10605            self.expect(TokenType::LParen)?;
10606            let idx_name = self.expect_identifier_with_quoted()?;
10607            self.expect(TokenType::RParen)?;
10608            Some(idx_name.name)
10609        } else {
10610            None
10611        };
10612
10613        // Check for optional alias (with or without AS)
10614        let (alias, alias_explicit_as) = if force_index.is_none() && self.match_token(TokenType::As)
10615        {
10616            (Some(self.expect_identifier_with_quoted()?), true)
10617        } else if force_index.is_none()
10618            && self.is_identifier_token()
10619            && !self.check(TokenType::Using)
10620            && !self.check(TokenType::Where)
10621            && !self.check(TokenType::Inner)
10622            && !self.check(TokenType::Left)
10623            && !self.check(TokenType::Right)
10624            && !self.check(TokenType::Cross)
10625            && !self.check(TokenType::Full)
10626            && !self.check(TokenType::Join)
10627            && !self.check_identifier("FORCE")
10628        {
10629            (Some(self.expect_identifier_with_quoted()?), false)
10630        } else {
10631            (None, false)
10632        };
10633
10634        // Parse JOINs for MySQL multi-table: DELETE t1 FROM t1 LEFT JOIN t2 ON ...
10635        let mut joins = self.parse_joins()?;
10636
10637        // Parse USING clause (PostgreSQL/DuckDB/MySQL)
10638        let mut using = Vec::new();
10639        if self.match_token(TokenType::Using) {
10640            loop {
10641                // Check for subquery: USING (SELECT ...) AS ... or (VALUES ...) AS ...
10642                if self.check(TokenType::LParen) {
10643                    // Check if next token after ( is VALUES
10644                    let is_values = self.current + 1 < self.tokens.len()
10645                        && self.tokens[self.current + 1].token_type == TokenType::Values;
10646                    let subquery = if is_values {
10647                        // Parse (VALUES ...) as parenthesized VALUES
10648                        self.skip(); // consume (
10649                        let values = self.parse_values()?;
10650                        self.expect(TokenType::RParen)?;
10651                        Expression::Paren(Box::new(Paren {
10652                            this: values,
10653                            trailing_comments: Vec::new(),
10654                        }))
10655                    } else {
10656                        // Parse as subquery (SELECT ...) or other expression
10657                        self.parse_primary()?
10658                    };
10659                    // Parse alias
10660                    let using_alias = if self.match_token(TokenType::As) {
10661                        let alias_name = self.expect_identifier_with_quoted()?;
10662                        // Check for column aliases: AS name(col1, col2)
10663                        let col_aliases = if self.match_token(TokenType::LParen) {
10664                            let aliases = self.parse_identifier_list()?;
10665                            self.expect(TokenType::RParen)?;
10666                            aliases
10667                        } else {
10668                            Vec::new()
10669                        };
10670                        Some((alias_name, col_aliases))
10671                    } else {
10672                        None
10673                    };
10674                    // Create a TableRef from the subquery with alias
10675                    let mut tref = TableRef::new("");
10676                    if let Some((alias_name, col_aliases)) = using_alias {
10677                        tref.alias = Some(alias_name);
10678                        tref.alias_explicit_as = true;
10679                        tref.column_aliases = col_aliases;
10680                    }
10681                    // Store the subquery in the table reference using hints (as a hack)
10682                    // Actually, we need a better approach - use the table ref hints to store the subquery
10683                    tref.hints = vec![subquery];
10684                    using.push(tref);
10685                } else {
10686                    let using_table = self.expect_identifier_with_quoted()?;
10687                    let mut using_ref = TableRef::from_identifier(using_table);
10688
10689                    // Check for JOINs: USING t1 INNER JOIN t2 INNER JOIN t3
10690                    if self.check_join_keyword() {
10691                        // Parse JOINs as part of USING
10692                        using.push(using_ref);
10693                        let mut using_joins = self.parse_joins()?;
10694                        joins.append(&mut using_joins);
10695                        break;
10696                    }
10697
10698                    // Optional alias for using table
10699                    if self.match_token(TokenType::As) {
10700                        using_ref.alias = Some(self.expect_identifier_with_quoted()?);
10701                        using_ref.alias_explicit_as = true;
10702                    } else if self.is_identifier_token()
10703                        && !self.check(TokenType::Comma)
10704                        && !self.check(TokenType::Where)
10705                    {
10706                        using_ref.alias = Some(self.expect_identifier_with_quoted()?);
10707                    }
10708                    using.push(using_ref);
10709                }
10710                if !self.match_token(TokenType::Comma) {
10711                    break;
10712                }
10713            }
10714        }
10715
10716        // ClickHouse: IN PARTITION 'partition_id' clause before WHERE
10717        if matches!(
10718            self.config.dialect,
10719            Some(crate::dialects::DialectType::ClickHouse)
10720        ) && self.check(TokenType::In)
10721            && self
10722                .peek_nth(1)
10723                .is_some_and(|t| t.text.eq_ignore_ascii_case("PARTITION"))
10724        {
10725            self.skip(); // consume IN
10726            self.skip(); // consume PARTITION
10727                         // Consume partition expression (string or identifier)
10728            let _partition = self.parse_primary()?;
10729        }
10730
10731        // Parse OUTPUT clause (TSQL) - may have been parsed early (before FROM)
10732        let output = if early_output.is_some() {
10733            early_output
10734        } else if self.match_token(TokenType::Output) {
10735            Some(self.parse_output_clause()?)
10736        } else {
10737            None
10738        };
10739
10740        let where_clause = if self.match_token(TokenType::Where) {
10741            Some(Where {
10742                this: self.parse_expression()?,
10743            })
10744        } else {
10745            None
10746        };
10747
10748        // Parse ORDER BY clause (MySQL)
10749        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
10750            Some(self.parse_order_by()?)
10751        } else {
10752            None
10753        };
10754
10755        // Parse LIMIT clause (MySQL)
10756        let limit = if self.match_token(TokenType::Limit) {
10757            Some(self.parse_expression()?)
10758        } else {
10759            None
10760        };
10761
10762        // Parse RETURNING clause (PostgreSQL)
10763        let returning = if self.match_token(TokenType::Returning) {
10764            self.parse_select_expressions()?
10765        } else {
10766            Vec::new()
10767        };
10768
10769        Ok(Expression::Delete(Box::new(Delete {
10770            table,
10771            on_cluster,
10772            alias,
10773            alias_explicit_as,
10774            using,
10775            where_clause,
10776            output,
10777            leading_comments,
10778            with: None,
10779            limit,
10780            order_by,
10781            returning,
10782            tables,
10783            tables_from_using,
10784            joins,
10785            force_index,
10786            no_from: !_has_from,
10787        })))
10788    }
10789
10790    // ==================== DDL Parsing ====================
10791
10792    /// Parse a CREATE statement
10793    fn parse_create(&mut self) -> Result<Expression> {
10794        let create_pos = self.current; // position of CREATE token
10795        let create_token = self.expect(TokenType::Create)?;
10796        let leading_comments = create_token.comments;
10797
10798        // Handle OR REPLACE / OR ALTER (TSQL)
10799        let or_replace = self.match_keywords(&[TokenType::Or, TokenType::Replace]);
10800        let or_alter = !or_replace && self.match_text_seq(&["OR", "ALTER"]);
10801
10802        // Handle TEMPORARY
10803        let temporary = self.match_token(TokenType::Temporary);
10804
10805        // Handle MATERIALIZED
10806        let materialized = self.match_token(TokenType::Materialized);
10807
10808        // Parse MySQL-specific CREATE VIEW options: ALGORITHM, DEFINER, SQL SECURITY
10809        // CREATE ALGORITHM=... DEFINER=... SQL SECURITY DEFINER VIEW ...
10810        let mut algorithm: Option<String> = None;
10811        let mut definer: Option<String> = None;
10812        let mut security: Option<FunctionSecurity> = None;
10813
10814        while self.match_identifier("ALGORITHM")
10815            || self.match_identifier("DEFINER")
10816            || self.match_identifier("SQL")
10817        {
10818            let option_name = self.previous().text.to_ascii_uppercase();
10819
10820            if option_name == "ALGORITHM" && self.match_token(TokenType::Eq) {
10821                // ALGORITHM=UNDEFINED|MERGE|TEMPTABLE
10822                let value = self.expect_identifier_or_keyword()?;
10823                algorithm = Some(value.to_ascii_uppercase());
10824            } else if option_name == "DEFINER" && self.match_token(TokenType::Eq) {
10825                // DEFINER=user@host (can include @ and %)
10826                let mut definer_value = String::new();
10827                while !self.is_at_end()
10828                    && !self.check(TokenType::View)
10829                    && !self.check_identifier("ALGORITHM")
10830                    && !self.check_identifier("DEFINER")
10831                    && !self.check_identifier("SQL")
10832                    && !self.check_identifier("SECURITY")
10833                {
10834                    definer_value.push_str(&self.advance().text);
10835                }
10836                definer = Some(definer_value);
10837            } else if option_name == "SQL" && self.match_identifier("SECURITY") {
10838                // SQL SECURITY DEFINER/INVOKER
10839                if self.match_identifier("DEFINER") {
10840                    security = Some(FunctionSecurity::Definer);
10841                } else if self.match_identifier("INVOKER") {
10842                    security = Some(FunctionSecurity::Invoker);
10843                }
10844            }
10845        }
10846
10847        // Handle SECURE modifier for VIEW (Snowflake)
10848        let secure = self.match_identifier("SECURE");
10849
10850        // Handle table modifiers: DYNAMIC, ICEBERG, EXTERNAL, HYBRID, TRANSIENT (Snowflake), UNLOGGED (PostgreSQL)
10851        let mut table_modifier: Option<String> = if self.check_identifier("DYNAMIC") {
10852            self.skip();
10853            Some("DYNAMIC".to_string())
10854        } else if self.check_identifier("ICEBERG") {
10855            self.skip();
10856            Some("ICEBERG".to_string())
10857        } else if self.check_identifier("EXTERNAL") {
10858            self.skip();
10859            Some("EXTERNAL".to_string())
10860        } else if self.check_identifier("HYBRID") {
10861            self.skip();
10862            Some("HYBRID".to_string())
10863        } else if self.check_identifier("TRANSIENT") {
10864            self.skip();
10865            Some("TRANSIENT".to_string())
10866        } else if self.check_identifier("UNLOGGED") {
10867            self.skip();
10868            Some("UNLOGGED".to_string())
10869        } else if self.check_identifier("DICTIONARY") {
10870            self.skip();
10871            Some("DICTIONARY".to_string())
10872        } else if self.check(TokenType::Dictionary) {
10873            self.skip();
10874            Some("DICTIONARY".to_string())
10875        } else {
10876            None
10877        };
10878
10879        // Teradata: SET/MULTISET/VOLATILE/GLOBAL TEMPORARY modifiers before TABLE
10880        if matches!(
10881            self.config.dialect,
10882            Some(crate::dialects::DialectType::Teradata)
10883        ) {
10884            let mut parts = Vec::new();
10885            loop {
10886                if self.match_token(TokenType::Set) {
10887                    parts.push(self.previous().text.to_ascii_uppercase());
10888                } else if self.match_identifier("MULTISET") {
10889                    parts.push(self.previous().text.to_ascii_uppercase());
10890                } else if self.match_identifier("VOLATILE") {
10891                    parts.push(self.previous().text.to_ascii_uppercase());
10892                } else if self.match_identifier("GLOBAL") {
10893                    parts.push(self.previous().text.to_ascii_uppercase());
10894                } else if self.match_token(TokenType::Temporary) {
10895                    parts.push(self.previous().text.to_ascii_uppercase());
10896                } else {
10897                    break;
10898                }
10899            }
10900            if !parts.is_empty() {
10901                table_modifier = Some(parts.join(" "));
10902            }
10903        }
10904
10905        if table_modifier.as_deref() == Some("DICTIONARY") {
10906            return self.parse_create_table(
10907                or_replace,
10908                temporary,
10909                leading_comments,
10910                table_modifier.as_deref(),
10911            );
10912        }
10913
10914        match self.peek().token_type {
10915            TokenType::Table => {
10916                // Check if this is CREATE TABLE FUNCTION (BigQuery)
10917                if self.current + 1 < self.tokens.len()
10918                    && self.tokens[self.current + 1].token_type == TokenType::Function
10919                {
10920                    self.skip(); // consume TABLE
10921                    return self.parse_create_function(or_replace, or_alter, temporary, true);
10922                }
10923                let modifier = if materialized {
10924                    Some("MATERIALIZED")
10925                } else {
10926                    table_modifier.as_deref()
10927                };
10928                self.parse_create_table(or_replace, temporary, leading_comments, modifier)
10929            }
10930            TokenType::Dictionary => {
10931                self.parse_create_table(or_replace, temporary, leading_comments, Some("DICTIONARY"))
10932            }
10933            TokenType::View => self.parse_create_view(
10934                or_replace,
10935                or_alter,
10936                materialized,
10937                temporary,
10938                algorithm,
10939                definer,
10940                security,
10941                secure,
10942            ),
10943            TokenType::Unique => {
10944                self.skip(); // consume UNIQUE
10945                             // Check for CLUSTERED/NONCLUSTERED after UNIQUE (TSQL)
10946                let clustered = if self.check_identifier("CLUSTERED") {
10947                    self.skip();
10948                    Some("CLUSTERED".to_string())
10949                } else if self.check_identifier("NONCLUSTERED") {
10950                    self.skip();
10951                    Some("NONCLUSTERED".to_string())
10952                } else {
10953                    None
10954                };
10955                // Check for COLUMNSTORE (TSQL: CREATE UNIQUE NONCLUSTERED COLUMNSTORE INDEX)
10956                if self.check_identifier("COLUMNSTORE") {
10957                    self.skip();
10958                    // Prepend COLUMNSTORE to clustered
10959                    let clustered = clustered
10960                        .map(|c| format!("{} COLUMNSTORE", c))
10961                        .or_else(|| Some("COLUMNSTORE".to_string()));
10962                    self.parse_create_index_with_clustered(true, clustered)
10963                } else {
10964                    self.parse_create_index_with_clustered(true, clustered)
10965                }
10966            }
10967            TokenType::Index => self.parse_create_index_with_clustered(false, None),
10968            TokenType::Schema => self.parse_create_schema(leading_comments),
10969            TokenType::Database => self.parse_create_database(),
10970            TokenType::Function => {
10971                self.parse_create_function(or_replace, or_alter, temporary, false)
10972            }
10973            TokenType::Procedure => self.parse_create_procedure(or_replace, or_alter),
10974            TokenType::Sequence => self.parse_create_sequence(temporary, or_replace),
10975            TokenType::Trigger => {
10976                self.parse_create_trigger(or_replace, or_alter, false, create_pos)
10977            }
10978            TokenType::Constraint => {
10979                self.skip(); // consume CONSTRAINT
10980                self.parse_create_trigger(or_replace, or_alter, true, create_pos)
10981            }
10982            TokenType::Type => self.parse_create_type(),
10983            TokenType::Domain => self.parse_create_domain(),
10984            _ => {
10985                // Handle TSQL CLUSTERED/NONCLUSTERED [COLUMNSTORE] INDEX
10986                if self.check_identifier("CLUSTERED") || self.check_identifier("NONCLUSTERED") {
10987                    let clustered_text = self.advance().text.to_ascii_uppercase();
10988                    // Check for COLUMNSTORE after CLUSTERED/NONCLUSTERED
10989                    let clustered = if self.check_identifier("COLUMNSTORE") {
10990                        self.skip();
10991                        Some(format!("{} COLUMNSTORE", clustered_text))
10992                    } else {
10993                        Some(clustered_text)
10994                    };
10995                    return self.parse_create_index_with_clustered(false, clustered);
10996                }
10997                // Handle TSQL COLUMNSTORE INDEX (without CLUSTERED/NONCLUSTERED prefix)
10998                if self.check_identifier("COLUMNSTORE") && {
10999                    let pos = self.current;
11000                    let result = pos + 1 < self.tokens.len()
11001                        && self.tokens[pos + 1].token_type == TokenType::Index;
11002                    result
11003                } {
11004                    self.skip(); // consume COLUMNSTORE
11005                                 // COLUMNSTORE without prefix implies NONCLUSTERED
11006                    return self.parse_create_index_with_clustered(
11007                        false,
11008                        Some("NONCLUSTERED COLUMNSTORE".to_string()),
11009                    );
11010                }
11011                // Handle identifiers that aren't keywords: TAG, STAGE, STREAM, etc.
11012                if self.check_identifier("TAG") {
11013                    return self.parse_create_tag(or_replace);
11014                }
11015                if self.check_identifier("STAGE") {
11016                    return self.parse_create_stage(or_replace, temporary);
11017                }
11018                if self.check_identifier("STREAM") {
11019                    return self.parse_create_stream(or_replace);
11020                }
11021                if self.check_identifier("TASK") {
11022                    return self.parse_create_task(or_replace);
11023                }
11024                if (self.check_identifier("FILE") || self.check(TokenType::File)) && {
11025                    let next = self.current + 1;
11026                    next < self.tokens.len()
11027                        && (self.tokens[next].text.eq_ignore_ascii_case("FORMAT"))
11028                } {
11029                    return self.parse_create_file_format(or_replace, temporary);
11030                }
11031                // TSQL: CREATE SYNONYM name FOR target
11032                if self.check_identifier("SYNONYM") {
11033                    self.skip(); // consume SYNONYM
11034                    let name = self.parse_table_ref()?;
11035                    self.expect(TokenType::For)?;
11036                    let target = self.parse_table_ref()?;
11037                    return Ok(Expression::CreateSynonym(Box::new(
11038                        crate::expressions::CreateSynonym { name, target },
11039                    )));
11040                }
11041                // Fall back to Raw for unrecognized CREATE targets
11042                // (e.g., CREATE WAREHOUSE, CREATE STREAMLIT, CREATE STORAGE INTEGRATION, etc.)
11043                {
11044                    let start = self.current;
11045                    while !self.is_at_end() && !self.check(TokenType::Semicolon) {
11046                        self.skip();
11047                    }
11048                    let sql = self.tokens_to_sql(start, self.current);
11049                    let mut prefix = String::from("CREATE");
11050                    if or_replace {
11051                        prefix.push_str(" OR REPLACE");
11052                    }
11053                    if temporary {
11054                        prefix.push_str(" TEMPORARY");
11055                    }
11056                    if materialized {
11057                        prefix.push_str(" MATERIALIZED");
11058                    }
11059                    prefix.push(' ');
11060                    prefix.push_str(&sql);
11061                    Ok(Expression::Raw(Raw { sql: prefix }))
11062                }
11063            }
11064        }
11065    }
11066
11067    /// Parse CREATE TABLE
11068    fn parse_create_table(
11069        &mut self,
11070        or_replace: bool,
11071        temporary: bool,
11072        leading_comments: Vec<String>,
11073        table_modifier: Option<&str>,
11074    ) -> Result<Expression> {
11075        if table_modifier == Some("DICTIONARY") {
11076            let _ = self.match_token(TokenType::Dictionary);
11077        } else {
11078            self.expect(TokenType::Table)?;
11079        }
11080
11081        // Handle IF NOT EXISTS
11082        let if_not_exists =
11083            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
11084
11085        let is_special_modifier = matches!(
11086            table_modifier,
11087            Some(
11088                "DYNAMIC"
11089                    | "ICEBERG"
11090                    | "EXTERNAL"
11091                    | "HYBRID"
11092                    | "UNLOGGED"
11093                    | "DICTIONARY"
11094                    | "MATERIALIZED"
11095            )
11096        ) || (table_modifier.is_some()
11097            && matches!(
11098                self.config.dialect,
11099                Some(crate::dialects::DialectType::Teradata)
11100            ));
11101        let is_clickhouse = matches!(
11102            self.config.dialect,
11103            Some(crate::dialects::DialectType::ClickHouse)
11104        );
11105
11106        // Parse table name
11107        let name = self.parse_table_ref()?;
11108
11109        // ClickHouse: UUID 'xxx' clause after table name
11110        let uuid = if matches!(
11111            self.config.dialect,
11112            Some(crate::dialects::DialectType::ClickHouse)
11113        ) && self.check_identifier("UUID")
11114        {
11115            self.skip(); // consume UUID
11116            let uuid_token = self.advance().clone();
11117            // Strip surrounding quotes from the UUID string
11118            let uuid_text = uuid_token.text.trim_matches('\'').to_string();
11119            Some(uuid_text)
11120        } else {
11121            None
11122        };
11123
11124        // ClickHouse: ON CLUSTER clause
11125        let on_cluster = self.parse_on_cluster_clause()?;
11126
11127        // Teradata: options after name before column list
11128        let teradata_post_name_options = if matches!(
11129            self.config.dialect,
11130            Some(crate::dialects::DialectType::Teradata)
11131        ) {
11132            self.parse_teradata_post_name_options()
11133        } else {
11134            Vec::new()
11135        };
11136
11137        // Handle PARTITION OF parent_table [(column_defs)] [FOR VALUES spec | DEFAULT] [PARTITION BY ...]
11138        if self.match_keywords(&[TokenType::Partition, TokenType::Of]) {
11139            return self.parse_create_table_partition_of(
11140                name,
11141                if_not_exists,
11142                temporary,
11143                or_replace,
11144                table_modifier,
11145                leading_comments,
11146            );
11147        }
11148
11149        // ClickHouse: EMPTY AS source_table — create empty table from source
11150        if matches!(
11151            self.config.dialect,
11152            Some(crate::dialects::DialectType::ClickHouse)
11153        ) && self.check_identifier("EMPTY")
11154        {
11155            if self.check_next(TokenType::As) {
11156                self.skip(); // consume EMPTY
11157                self.skip(); // consume AS
11158                             // Consume rest as Command
11159                let start = self.current;
11160                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
11161                    self.skip();
11162                }
11163                let rest_sql = self.tokens_to_sql(start, self.current);
11164                let mut prefix = String::from("CREATE TABLE");
11165                if if_not_exists {
11166                    prefix.push_str(" IF NOT EXISTS");
11167                }
11168                prefix.push(' ');
11169                prefix.push_str(&name.name.name);
11170                prefix.push_str(" EMPTY AS ");
11171                prefix.push_str(&rest_sql);
11172                return Ok(Expression::Raw(Raw { sql: prefix }));
11173            }
11174        }
11175
11176        // Handle [SHALLOW | DEEP] CLONE source_table [AT(...) | BEFORE(...)]
11177        // Databricks/Delta Lake uses SHALLOW CLONE / DEEP CLONE
11178        // Snowflake uses just CLONE (which is equivalent to DEEP CLONE)
11179        let shallow_clone = self.check_identifier("SHALLOW");
11180        let deep_clone = self.check_identifier("DEEP");
11181        if shallow_clone || deep_clone {
11182            self.skip(); // consume SHALLOW or DEEP
11183        }
11184        // Also handle COPY (BigQuery: CREATE TABLE ... COPY source_table)
11185        // But NOT "COPY GRANTS" which is a Snowflake property
11186        let is_copy = self.check(TokenType::Copy) && !self.check_next_identifier("GRANTS");
11187        if self.check_identifier("CLONE") || is_copy {
11188            self.skip(); // consume CLONE or COPY
11189                         // ClickHouse: CLONE AS source_table (AS is part of the syntax, not an alias)
11190            if matches!(
11191                self.config.dialect,
11192                Some(crate::dialects::DialectType::ClickHouse)
11193            ) {
11194                let _ = self.match_token(TokenType::As);
11195            }
11196            let source = self.parse_table_ref()?;
11197            // Parse optional AT or BEFORE time travel clause
11198            // Note: BEFORE is a keyword token, AT is an identifier
11199            let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
11200                let keyword = self.previous().text.to_ascii_uppercase();
11201                self.expect(TokenType::LParen)?;
11202                // Parse the content: OFFSET => value or TIMESTAMP => value
11203                let mut result = format!("{} (", keyword);
11204                let mut prev_token_type: Option<TokenType> = None;
11205                let mut paren_depth = 1;
11206                while !self.is_at_end() && paren_depth > 0 {
11207                    let token = self.advance();
11208                    if token.token_type == TokenType::LParen {
11209                        paren_depth += 1;
11210                    } else if token.token_type == TokenType::RParen {
11211                        paren_depth -= 1;
11212                        if paren_depth == 0 {
11213                            break;
11214                        }
11215                    }
11216                    let needs_space = !result.ends_with('(')
11217                        && prev_token_type != Some(TokenType::Arrow)
11218                        && prev_token_type != Some(TokenType::Dash)
11219                        && prev_token_type != Some(TokenType::LParen)
11220                        && prev_token_type != Some(TokenType::Comma) // comma already adds trailing space
11221                        && token.token_type != TokenType::LParen; // no space before (
11222                    if needs_space
11223                        && token.token_type != TokenType::RParen
11224                        && token.token_type != TokenType::Comma
11225                    {
11226                        result.push(' ');
11227                    }
11228                    // Properly quote string literals
11229                    if token.token_type == TokenType::String {
11230                        result.push('\'');
11231                        result.push_str(&token.text.replace('\'', "''"));
11232                        result.push('\'');
11233                    } else {
11234                        result.push_str(&token.text);
11235                    }
11236                    if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma
11237                    {
11238                        result.push(' ');
11239                    }
11240                    prev_token_type = Some(token.token_type);
11241                }
11242                result.push(')');
11243                Some(Expression::Raw(Raw { sql: result }))
11244            } else {
11245                None
11246            };
11247            // Return the CLONE table immediately
11248            return Ok(Expression::CreateTable(Box::new(CreateTable {
11249                name,
11250                on_cluster: on_cluster.clone(),
11251                columns: Vec::new(),
11252                constraints: Vec::new(),
11253                if_not_exists,
11254                temporary,
11255                or_replace,
11256                table_modifier: table_modifier.map(|s| s.to_string()),
11257                as_select: None,
11258                as_select_parenthesized: false,
11259                on_commit: None,
11260                clone_source: Some(source),
11261                clone_at_clause: at_clause,
11262                shallow_clone,
11263                is_copy,
11264                leading_comments,
11265                with_properties: Vec::new(),
11266                teradata_post_name_options: teradata_post_name_options.clone(),
11267                with_data: None,
11268                with_statistics: None,
11269                teradata_indexes: Vec::new(),
11270                with_cte: None,
11271                properties: Vec::new(),
11272                partition_of: None,
11273                post_table_properties: Vec::new(),
11274                mysql_table_options: Vec::new(),
11275                inherits: Vec::new(),
11276                on_property: None,
11277                copy_grants: false,
11278                using_template: None,
11279                rollup: None,
11280                uuid: uuid.clone(),
11281            })));
11282        }
11283
11284        // Handle WITH properties before columns/AS (e.g., CREATE TABLE z WITH (FORMAT='parquet') AS SELECT 1)
11285        let with_properties = if self.match_token(TokenType::With) {
11286            self.parse_with_properties()?
11287        } else {
11288            Vec::new()
11289        };
11290
11291        // Snowflake: COPY GRANTS clause (before column list or AS)
11292        let copy_grants = self.match_text_seq(&["COPY", "GRANTS"]);
11293
11294        // Snowflake: USING TEMPLATE (expr) - allows schema inference from a query
11295        let using_template = if self.match_text_seq(&["USING", "TEMPLATE"]) {
11296            Some(Box::new(self.parse_primary()?))
11297        } else {
11298            None
11299        };
11300
11301        // If we have USING TEMPLATE, return early since it replaces AS SELECT
11302        if using_template.is_some() {
11303            return Ok(Expression::CreateTable(Box::new(CreateTable {
11304                name,
11305                on_cluster: on_cluster.clone(),
11306                columns: Vec::new(),
11307                constraints: Vec::new(),
11308                if_not_exists,
11309                temporary,
11310                or_replace,
11311                table_modifier: table_modifier.map(|s| s.to_string()),
11312                as_select: None,
11313                as_select_parenthesized: false,
11314                on_commit: None,
11315                clone_source: None,
11316                clone_at_clause: None,
11317                shallow_clone: false,
11318                is_copy: false,
11319                leading_comments,
11320                with_properties,
11321                teradata_post_name_options: teradata_post_name_options.clone(),
11322                with_data: None,
11323                with_statistics: None,
11324                teradata_indexes: Vec::new(),
11325                with_cte: None,
11326                properties: Vec::new(),
11327                partition_of: None,
11328                post_table_properties: Vec::new(),
11329                mysql_table_options: Vec::new(),
11330                inherits: Vec::new(),
11331                on_property: None,
11332                copy_grants,
11333                using_template,
11334                rollup: None,
11335                uuid: uuid.clone(),
11336            })));
11337        }
11338
11339        // Redshift: Parse DISTKEY, SORTKEY, DISTSTYLE, BACKUP before AS SELECT (CTAS without columns)
11340        // This handles: CREATE TABLE t BACKUP YES|NO AS SELECT ...
11341        let mut redshift_ctas_properties: Vec<Expression> = Vec::new();
11342        loop {
11343            if self.match_identifier("DISTKEY") {
11344                // DISTKEY(column)
11345                if self.match_token(TokenType::LParen) {
11346                    let col = self.expect_identifier()?;
11347                    self.expect(TokenType::RParen)?;
11348                    redshift_ctas_properties.push(Expression::DistKeyProperty(Box::new(
11349                        DistKeyProperty {
11350                            this: Box::new(Expression::boxed_column(Column {
11351                                name: Identifier::new(col),
11352                                table: None,
11353                                join_mark: false,
11354                                trailing_comments: Vec::new(),
11355                                span: None,
11356                                inferred_type: None,
11357                            })),
11358                        },
11359                    )));
11360                }
11361            } else if self.check_identifier("COMPOUND") || self.check_identifier("INTERLEAVED") {
11362                // COMPOUND SORTKEY(col, ...) or INTERLEAVED SORTKEY(col, ...)
11363                let modifier = self.advance().text.to_ascii_uppercase();
11364                if self.match_identifier("SORTKEY") && self.match_token(TokenType::LParen) {
11365                    let mut cols = Vec::new();
11366                    loop {
11367                        let col = self.expect_identifier()?;
11368                        cols.push(Expression::boxed_column(Column {
11369                            name: Identifier::new(col),
11370                            table: None,
11371                            join_mark: false,
11372                            trailing_comments: Vec::new(),
11373                            span: None,
11374                            inferred_type: None,
11375                        }));
11376                        if !self.match_token(TokenType::Comma) {
11377                            break;
11378                        }
11379                    }
11380                    self.expect(TokenType::RParen)?;
11381                    let compound_value = if modifier == "COMPOUND" {
11382                        Some(Box::new(Expression::Boolean(BooleanLiteral {
11383                            value: true,
11384                        })))
11385                    } else {
11386                        None
11387                    };
11388                    redshift_ctas_properties.push(Expression::SortKeyProperty(Box::new(
11389                        SortKeyProperty {
11390                            this: Box::new(Expression::Tuple(Box::new(Tuple {
11391                                expressions: cols,
11392                            }))),
11393                            compound: compound_value,
11394                        },
11395                    )));
11396                }
11397            } else if self.match_identifier("SORTKEY") {
11398                // SORTKEY(column, ...)
11399                if self.match_token(TokenType::LParen) {
11400                    let mut cols = Vec::new();
11401                    loop {
11402                        let col = self.expect_identifier()?;
11403                        cols.push(Expression::boxed_column(Column {
11404                            name: Identifier::new(col),
11405                            table: None,
11406                            join_mark: false,
11407                            trailing_comments: Vec::new(),
11408                            span: None,
11409                            inferred_type: None,
11410                        }));
11411                        if !self.match_token(TokenType::Comma) {
11412                            break;
11413                        }
11414                    }
11415                    self.expect(TokenType::RParen)?;
11416                    redshift_ctas_properties.push(Expression::SortKeyProperty(Box::new(
11417                        SortKeyProperty {
11418                            this: Box::new(Expression::Tuple(Box::new(Tuple {
11419                                expressions: cols,
11420                            }))),
11421                            compound: None,
11422                        },
11423                    )));
11424                }
11425            } else if self.match_identifier("DISTSTYLE") {
11426                // DISTSTYLE ALL|EVEN|AUTO|KEY
11427                if self.match_texts(&["ALL", "EVEN", "AUTO", "KEY"]) {
11428                    let style = self.previous().text.to_ascii_uppercase();
11429                    redshift_ctas_properties.push(Expression::DistStyleProperty(Box::new(
11430                        DistStyleProperty {
11431                            this: Box::new(Expression::Var(Box::new(Var { this: style }))),
11432                        },
11433                    )));
11434                }
11435            } else if self.match_identifier("BACKUP") {
11436                // BACKUP YES|NO
11437                if self.match_texts(&["YES", "NO"]) {
11438                    let value = self.previous().text.to_ascii_uppercase();
11439                    redshift_ctas_properties.push(Expression::BackupProperty(Box::new(
11440                        BackupProperty {
11441                            this: Box::new(Expression::Var(Box::new(Var { this: value }))),
11442                        },
11443                    )));
11444                }
11445            } else {
11446                break;
11447            }
11448        }
11449
11450        // Check for AS SELECT (CTAS)
11451        if self.match_token(TokenType::As) {
11452            // ClickHouse: CREATE TABLE t AS other_table [ENGINE = ...] — copy structure from another table
11453            // Also: CREATE TABLE t AS func_name(args...) — table from function (e.g., remote, merge)
11454            // Detect when AS is followed by an identifier (not SELECT/WITH/LParen)
11455            if is_clickhouse
11456                && !self.check(TokenType::Select)
11457                && !self.check(TokenType::With)
11458                && !self.check(TokenType::LParen)
11459                && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
11460            {
11461                // Check if this is AS func_name(...) — table function
11462                let is_table_func = self.current + 1 < self.tokens.len()
11463                    && self.tokens[self.current + 1].token_type == TokenType::LParen;
11464                let source = if is_table_func {
11465                    // Parse as expression to consume function call with arguments
11466                    self.parse_primary()?;
11467                    let mut table_properties: Vec<Expression> = Vec::new();
11468                    self.parse_clickhouse_table_properties(&mut table_properties)?;
11469                    return Ok(Expression::CreateTable(Box::new(CreateTable {
11470                        name,
11471                        on_cluster: on_cluster.clone(),
11472                        columns: Vec::new(),
11473                        constraints: Vec::new(),
11474                        if_not_exists,
11475                        temporary,
11476                        or_replace,
11477                        table_modifier: table_modifier.map(|s| s.to_string()),
11478                        as_select: None,
11479                        as_select_parenthesized: false,
11480                        on_commit: None,
11481                        clone_source: None,
11482                        clone_at_clause: None,
11483                        shallow_clone: false,
11484                        is_copy: false,
11485                        leading_comments,
11486                        with_properties,
11487                        teradata_post_name_options: teradata_post_name_options.clone(),
11488                        with_data: None,
11489                        with_statistics: None,
11490                        teradata_indexes: Vec::new(),
11491                        with_cte: None,
11492                        properties: table_properties,
11493                        partition_of: None,
11494                        post_table_properties: redshift_ctas_properties,
11495                        mysql_table_options: Vec::new(),
11496                        inherits: Vec::new(),
11497                        on_property: None,
11498                        copy_grants,
11499                        using_template: None,
11500                        rollup: None,
11501                        uuid: uuid.clone(),
11502                    })));
11503                } else {
11504                    self.parse_table_ref()?
11505                };
11506                // Parse ClickHouse table properties after the source table
11507                let mut table_properties: Vec<Expression> = Vec::new();
11508                self.parse_clickhouse_table_properties(&mut table_properties)?;
11509                return Ok(Expression::CreateTable(Box::new(CreateTable {
11510                    name,
11511                    on_cluster: on_cluster.clone(),
11512                    columns: Vec::new(),
11513                    constraints: Vec::new(),
11514                    if_not_exists,
11515                    temporary,
11516                    or_replace,
11517                    table_modifier: table_modifier.map(|s| s.to_string()),
11518                    as_select: None,
11519                    as_select_parenthesized: false,
11520                    on_commit: None,
11521                    clone_source: Some(source),
11522                    clone_at_clause: None,
11523                    shallow_clone: false,
11524                    is_copy: false,
11525                    leading_comments,
11526                    with_properties,
11527                    teradata_post_name_options: teradata_post_name_options.clone(),
11528                    with_data: None,
11529                    with_statistics: None,
11530                    teradata_indexes: Vec::new(),
11531                    with_cte: None,
11532                    properties: table_properties,
11533                    partition_of: None,
11534                    post_table_properties: redshift_ctas_properties,
11535                    mysql_table_options: Vec::new(),
11536                    inherits: Vec::new(),
11537                    on_property: None,
11538                    copy_grants,
11539                    using_template: None,
11540                    rollup: None,
11541                    uuid: uuid.clone(),
11542                })));
11543            }
11544
11545            // The query can be:
11546            // - SELECT ... (simple case)
11547            // - (SELECT 1) UNION ALL (SELECT 2) (set operations)
11548            // - (WITH cte AS (SELECT 1) SELECT * FROM cte) (CTE in parens)
11549            let mut as_select_parenthesized = self.check(TokenType::LParen);
11550            let query = if as_select_parenthesized {
11551                // Parenthesized query - parse as expression which handles subqueries
11552                // Note: parse_primary will consume set operations like UNION internally
11553                let subquery = self.parse_primary()?;
11554                // If parse_primary returned a set operation, the outer parens weren't wrapping
11555                // the entire expression - they were part of the operands
11556                if matches!(
11557                    &subquery,
11558                    Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
11559                ) {
11560                    as_select_parenthesized = false;
11561                    subquery
11562                } else {
11563                    // Just a parenthesized query without set ops
11564                    // Keep the Subquery wrapper if it has limit/offset/order_by
11565                    if let Expression::Subquery(ref sq) = subquery {
11566                        if sq.limit.is_some() || sq.offset.is_some() || sq.order_by.is_some() {
11567                            // Keep the Subquery to preserve the modifiers
11568                            subquery
11569                        } else {
11570                            // Extract the inner query
11571                            if let Expression::Subquery(sq) = subquery {
11572                                sq.this
11573                            } else {
11574                                subquery
11575                            }
11576                        }
11577                    } else if let Expression::Paren(p) = subquery {
11578                        p.this
11579                    } else {
11580                        subquery
11581                    }
11582                }
11583            } else if self.check(TokenType::With) {
11584                // Handle WITH ... SELECT ...
11585                self.parse_statement()?
11586            } else {
11587                self.parse_select()?
11588            };
11589
11590            // Parse any trailing Teradata options like "WITH DATA", "NO PRIMARY INDEX", etc.
11591            let (with_data, with_statistics, teradata_indexes) =
11592                self.parse_teradata_table_options();
11593            let on_commit = if matches!(
11594                self.config.dialect,
11595                Some(crate::dialects::DialectType::Teradata)
11596            ) && self.check(TokenType::On)
11597                && self.check_next(TokenType::Commit)
11598            {
11599                self.skip(); // ON
11600                self.skip(); // COMMIT
11601                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
11602                    Some(OnCommit::PreserveRows)
11603                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
11604                    Some(OnCommit::DeleteRows)
11605                } else {
11606                    return Err(
11607                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
11608                    );
11609                }
11610            } else {
11611                None
11612            };
11613
11614            return Ok(Expression::CreateTable(Box::new(CreateTable {
11615                name,
11616                on_cluster: on_cluster.clone(),
11617                columns: Vec::new(),
11618                constraints: Vec::new(),
11619                if_not_exists,
11620                temporary,
11621                or_replace,
11622                table_modifier: table_modifier.map(|s| s.to_string()),
11623                as_select: Some(query),
11624                as_select_parenthesized,
11625                on_commit,
11626                clone_source: None,
11627                clone_at_clause: None,
11628                shallow_clone: false,
11629                is_copy: false,
11630                leading_comments,
11631                with_properties,
11632                teradata_post_name_options: teradata_post_name_options.clone(),
11633                with_data,
11634                with_statistics,
11635                teradata_indexes,
11636                with_cte: None,
11637                properties: Vec::new(),
11638                partition_of: None,
11639                post_table_properties: redshift_ctas_properties,
11640                mysql_table_options: Vec::new(),
11641                inherits: Vec::new(),
11642                on_property: None,
11643                copy_grants,
11644                using_template: None,
11645                rollup: None,
11646                uuid: uuid.clone(),
11647            })));
11648        }
11649
11650        // ClickHouse: allow table properties/AS SELECT without a column list
11651        if is_clickhouse && !self.check(TokenType::LParen) {
11652            let starts_props = self.check_identifier("ENGINE")
11653                || self.check(TokenType::Order)
11654                || self.check(TokenType::Sample)
11655                || self.check(TokenType::Settings)
11656                || self.check(TokenType::Comment)
11657                || self.check(TokenType::As);
11658
11659            if starts_props {
11660                let mut table_properties: Vec<Expression> = Vec::new();
11661                self.parse_clickhouse_table_properties(&mut table_properties)?;
11662
11663                let as_select = if self.match_token(TokenType::As) {
11664                    Some(self.parse_statement()?)
11665                } else {
11666                    None
11667                };
11668                let as_select_parenthesized = as_select.is_some();
11669
11670                if as_select.is_some() {
11671                    self.parse_clickhouse_table_properties(&mut table_properties)?;
11672                }
11673
11674                return Ok(Expression::CreateTable(Box::new(CreateTable {
11675                    name,
11676                    on_cluster: on_cluster.clone(),
11677                    columns: Vec::new(),
11678                    constraints: Vec::new(),
11679                    if_not_exists,
11680                    temporary,
11681                    or_replace,
11682                    table_modifier: table_modifier.map(|s| s.to_string()),
11683                    as_select,
11684                    as_select_parenthesized,
11685                    on_commit: None,
11686                    clone_source: None,
11687                    clone_at_clause: None,
11688                    shallow_clone: false,
11689                    is_copy: false,
11690                    leading_comments,
11691                    with_properties,
11692                    teradata_post_name_options: teradata_post_name_options.clone(),
11693                    with_data: None,
11694                    with_statistics: None,
11695                    teradata_indexes: Vec::new(),
11696                    with_cte: None,
11697                    properties: table_properties,
11698                    partition_of: None,
11699                    post_table_properties: Vec::new(),
11700                    mysql_table_options: Vec::new(),
11701                    inherits: Vec::new(),
11702                    on_property: None,
11703                    copy_grants,
11704                    using_template: None,
11705                    rollup: None,
11706                    uuid: uuid.clone(),
11707                })));
11708            }
11709        }
11710
11711        // For DYNAMIC/ICEBERG/EXTERNAL tables, columns might be optional (use AS SELECT or other syntax)
11712        // Check if we have a left paren for columns or if we're going straight to options
11713        if !self.check(TokenType::LParen) && is_special_modifier {
11714            // No columns - parse options and AS SELECT
11715            let mut extra_options = Vec::new();
11716            // Parse key=value options until AS or end
11717            // Note: WAREHOUSE is a keyword token type, so check for it explicitly
11718            while !self.is_at_end()
11719                && !self.check(TokenType::As)
11720                && !self.check(TokenType::Semicolon)
11721            {
11722                if self.is_identifier_token()
11723                    || self.is_safe_keyword_as_identifier()
11724                    || self.check(TokenType::Warehouse)
11725                {
11726                    let key = self.advance().text;
11727                    if self.match_token(TokenType::Eq) {
11728                        // Capture value
11729                        let value = if self.check(TokenType::String) {
11730                            let v = format!("'{}'", self.peek().text);
11731                            self.skip();
11732                            v
11733                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
11734                        {
11735                            self.advance().text
11736                        } else {
11737                            break;
11738                        };
11739                        extra_options.push((key, value));
11740                    } else {
11741                        // Just a keyword without value (like WAREHOUSE mywh)
11742                        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
11743                            let value = self.advance().text;
11744                            extra_options.push((key, value));
11745                        }
11746                    }
11747                } else {
11748                    break;
11749                }
11750            }
11751            // Check for AS SELECT
11752            let as_select = if self.match_token(TokenType::As) {
11753                Some(self.parse_statement()?)
11754            } else {
11755                None
11756            };
11757            return Ok(Expression::CreateTable(Box::new(CreateTable {
11758                name,
11759                on_cluster: on_cluster.clone(),
11760                columns: Vec::new(),
11761                constraints: Vec::new(),
11762                if_not_exists,
11763                temporary,
11764                or_replace,
11765                table_modifier: table_modifier.map(|s| s.to_string()),
11766                as_select,
11767                as_select_parenthesized: false,
11768                on_commit: None,
11769                clone_source: None,
11770                clone_at_clause: None,
11771                shallow_clone: false,
11772                is_copy: false,
11773                leading_comments,
11774                with_properties: extra_options,
11775                teradata_post_name_options: teradata_post_name_options.clone(),
11776                with_data: None,
11777                with_statistics: None,
11778                teradata_indexes: Vec::new(),
11779                with_cte: None,
11780                properties: Vec::new(),
11781                partition_of: None,
11782                post_table_properties: Vec::new(),
11783                mysql_table_options: Vec::new(),
11784                inherits: Vec::new(),
11785                on_property: None,
11786                copy_grants,
11787                using_template: None,
11788                rollup: None,
11789                uuid: uuid.clone(),
11790            })));
11791        }
11792
11793        // MySQL: CREATE TABLE A LIKE B (without parentheses)
11794        if self.check(TokenType::Like) {
11795            self.skip(); // consume LIKE
11796            let source_ref = self.parse_table_ref()?;
11797            return Ok(Expression::CreateTable(Box::new(CreateTable {
11798                name,
11799                on_cluster: on_cluster.clone(),
11800                columns: Vec::new(),
11801                constraints: vec![TableConstraint::Like {
11802                    source: source_ref,
11803                    options: Vec::new(),
11804                }],
11805                if_not_exists,
11806                temporary,
11807                or_replace,
11808                table_modifier: table_modifier.map(|s| s.to_string()),
11809                as_select: None,
11810                as_select_parenthesized: false,
11811                on_commit: None,
11812                clone_source: None,
11813                clone_at_clause: None,
11814                shallow_clone: false,
11815                is_copy: false,
11816                leading_comments,
11817                with_properties,
11818                teradata_post_name_options: teradata_post_name_options.clone(),
11819                with_data: None,
11820                with_statistics: None,
11821                teradata_indexes: Vec::new(),
11822                with_cte: None,
11823                properties: Vec::new(),
11824                partition_of: None,
11825                post_table_properties: Vec::new(),
11826                mysql_table_options: Vec::new(),
11827                inherits: Vec::new(),
11828                on_property: None,
11829                copy_grants,
11830                using_template: None,
11831                rollup: None,
11832                uuid: uuid.clone(),
11833            })));
11834        }
11835
11836        // Snowflake: CREATE TABLE a TAG (key='value', ...) without column definitions
11837        if self.match_keyword("TAG")
11838            || (self.match_token(TokenType::With) && self.match_keyword("TAG"))
11839        {
11840            let tags = self.parse_tags()?;
11841            return Ok(Expression::CreateTable(Box::new(CreateTable {
11842                name,
11843                on_cluster: on_cluster.clone(),
11844                columns: Vec::new(),
11845                constraints: vec![TableConstraint::Tags(tags)],
11846                if_not_exists,
11847                temporary,
11848                or_replace,
11849                table_modifier: table_modifier.map(|s| s.to_string()),
11850                as_select: None,
11851                as_select_parenthesized: false,
11852                on_commit: None,
11853                clone_source: None,
11854                clone_at_clause: None,
11855                shallow_clone: false,
11856                is_copy: false,
11857                leading_comments,
11858                with_properties,
11859                teradata_post_name_options: teradata_post_name_options.clone(),
11860                with_data: None,
11861                with_statistics: None,
11862                teradata_indexes: Vec::new(),
11863                with_cte: None,
11864                properties: Vec::new(),
11865                partition_of: None,
11866                post_table_properties: Vec::new(),
11867                mysql_table_options: Vec::new(),
11868                inherits: Vec::new(),
11869                on_property: None,
11870                copy_grants,
11871                using_template: None,
11872                rollup: None,
11873                uuid: uuid.clone(),
11874            })));
11875        }
11876
11877        // Hive/Spark/Databricks: CREATE TABLE t TBLPROPERTIES (...) without column definitions
11878        // Check for Hive-style table properties before expecting column definitions
11879        if self.check_identifier("TBLPROPERTIES")
11880            || self.check_identifier("LOCATION")
11881            || self.check_identifier("STORED")
11882            || self.check(TokenType::Row)
11883            || self.check(TokenType::Using)
11884            || self.check_identifier("CLUSTERED")
11885            || self.check_identifier("PARTITIONED")
11886            || self.check_identifier("COMMENT")
11887        {
11888            // Parse Hive table properties without column definitions
11889            let hive_properties = self.parse_hive_table_properties()?;
11890
11891            // Check for AS SELECT (CTAS) after properties
11892            let as_select = if self.match_token(TokenType::As) {
11893                Some(self.parse_statement()?)
11894            } else {
11895                None
11896            };
11897
11898            return Ok(Expression::CreateTable(Box::new(CreateTable {
11899                name,
11900                on_cluster: on_cluster.clone(),
11901                columns: Vec::new(),
11902                constraints: Vec::new(),
11903                if_not_exists,
11904                temporary,
11905                or_replace,
11906                table_modifier: table_modifier.map(|s| s.to_string()),
11907                as_select,
11908                as_select_parenthesized: false,
11909                on_commit: None,
11910                clone_source: None,
11911                clone_at_clause: None,
11912                shallow_clone: false,
11913                is_copy: false,
11914                leading_comments,
11915                with_properties,
11916                teradata_post_name_options: teradata_post_name_options.clone(),
11917                with_data: None,
11918                with_statistics: None,
11919                teradata_indexes: Vec::new(),
11920                with_cte: None,
11921                properties: hive_properties,
11922                partition_of: None,
11923                post_table_properties: Vec::new(),
11924                mysql_table_options: Vec::new(),
11925                inherits: Vec::new(),
11926                on_property: None,
11927                copy_grants,
11928                using_template: None,
11929                rollup: None,
11930                uuid: uuid.clone(),
11931            })));
11932        }
11933
11934        // Check if (SELECT ...) or (WITH ...) follows - this is CTAS without explicit AS keyword
11935        if self.check(TokenType::LParen) {
11936            let saved = self.current;
11937            self.skip(); // consume (
11938            let is_ctas = self.check(TokenType::Select) || self.check(TokenType::With);
11939            self.current = saved;
11940            if is_ctas {
11941                // Parse as subquery
11942                let subquery = self.parse_primary()?;
11943                let query = if let Expression::Subquery(sq) = subquery {
11944                    sq.this
11945                } else if let Expression::Paren(p) = subquery {
11946                    p.this
11947                } else {
11948                    subquery
11949                };
11950                return Ok(Expression::CreateTable(Box::new(CreateTable {
11951                    name,
11952                    on_cluster: on_cluster.clone(),
11953                    columns: Vec::new(),
11954                    constraints: Vec::new(),
11955                    if_not_exists,
11956                    temporary,
11957                    or_replace,
11958                    table_modifier: table_modifier.map(|s| s.to_string()),
11959                    as_select: Some(query),
11960                    as_select_parenthesized: true,
11961                    on_commit: None,
11962                    clone_source: None,
11963                    clone_at_clause: None,
11964                    shallow_clone: false,
11965                    is_copy: false,
11966                    leading_comments,
11967                    with_properties,
11968                    teradata_post_name_options: teradata_post_name_options.clone(),
11969                    with_data: None,
11970                    with_statistics: None,
11971                    teradata_indexes: Vec::new(),
11972                    with_cte: None,
11973                    properties: Vec::new(),
11974                    partition_of: None,
11975                    post_table_properties: Vec::new(),
11976                    mysql_table_options: Vec::new(),
11977                    inherits: Vec::new(),
11978                    on_property: None,
11979                    copy_grants,
11980                    using_template: None,
11981                    rollup: None,
11982                    uuid: uuid.clone(),
11983                })));
11984            }
11985        }
11986
11987        // BigQuery (and others): CREATE TABLE t PARTITION BY ... CLUSTER BY ... OPTIONS(...) AS (SELECT ...)
11988        // When there are no column definitions, skip straight to property/AS parsing
11989        let no_column_defs = !self.check(TokenType::LParen)
11990            && (self.check(TokenType::Partition)
11991                || self.check(TokenType::PartitionBy)
11992                || self.check(TokenType::Cluster)
11993                || self.check_identifier("OPTIONS")
11994                || self.check(TokenType::As));
11995
11996        // Parse column definitions
11997        if !no_column_defs {
11998            self.expect(TokenType::LParen)?;
11999        }
12000
12001        // For DYNAMIC TABLE, column list contains only names without types
12002        // e.g., CREATE DYNAMIC TABLE t (col1, col2, col3) TARGET_LAG=... AS SELECT ...
12003        let (columns, constraints) = if no_column_defs {
12004            (Vec::new(), Vec::new())
12005        } else if table_modifier == Some("DYNAMIC") {
12006            // Check if this looks like a simple column name list (just identifiers separated by commas)
12007            // by peeking ahead - if next token after identifier is comma or rparen, it's a name-only list
12008            let saved = self.current;
12009            let is_name_only_list =
12010                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
12011                    self.skip();
12012                    let result = self.check(TokenType::Comma) || self.check(TokenType::RParen);
12013                    self.current = saved;
12014                    result
12015                } else {
12016                    false
12017                };
12018
12019            if is_name_only_list {
12020                // Parse column names without types
12021                let mut cols = Vec::new();
12022                loop {
12023                    let name = self.expect_identifier_or_safe_keyword_with_quoted()?;
12024                    // Create a column def with an empty/placeholder type
12025                    let mut col_def = ColumnDef::new(
12026                        name.name.clone(),
12027                        DataType::Custom {
12028                            name: String::new(),
12029                        },
12030                    );
12031                    col_def.name = name;
12032                    cols.push(col_def);
12033                    if !self.match_token(TokenType::Comma) {
12034                        break;
12035                    }
12036                }
12037                (cols, Vec::new())
12038            } else {
12039                // Regular column definitions with types
12040                self.parse_column_definitions()?
12041            }
12042        } else {
12043            self.parse_column_definitions()?
12044        };
12045
12046        if !no_column_defs {
12047            self.expect(TokenType::RParen)?;
12048        }
12049
12050        // Parse COMMENT before WITH properties (Presto: CREATE TABLE x (...) COMMENT 'text' WITH (...))
12051        let pre_with_comment = if self.check(TokenType::Comment) {
12052            let saved = self.current;
12053            self.skip(); // consume COMMENT
12054            if self.check(TokenType::String) {
12055                let comment_text = self.advance().text.clone();
12056                Some(comment_text)
12057            } else {
12058                self.current = saved;
12059                None
12060            }
12061        } else {
12062            None
12063        };
12064
12065        // Handle WITH properties after columns (e.g., CREATE TABLE z (z INT) WITH (...))
12066        // But skip if this is WITH(SYSTEM_VERSIONING=...) which is handled by parse_post_table_properties
12067        let with_properties_after = if self.check(TokenType::With) {
12068            // Lookahead: check if this is WITH(SYSTEM_VERSIONING=...)
12069            let saved = self.current;
12070            self.skip(); // consume WITH
12071            let is_system_versioning = if self.check(TokenType::LParen) {
12072                let saved2 = self.current;
12073                self.skip(); // consume (
12074                let result = self.check_identifier("SYSTEM_VERSIONING");
12075                self.current = saved2; // retreat to before (
12076                result
12077            } else {
12078                false
12079            };
12080            if is_system_versioning {
12081                // Retreat back before WITH, let parse_post_table_properties handle it
12082                self.current = saved;
12083                Vec::new()
12084            } else {
12085                // Normal WITH properties parsing
12086                self.parse_with_properties()?
12087            }
12088        } else {
12089            Vec::new()
12090        };
12091
12092        // Combine properties from before and after columns
12093        let mut all_with_properties = with_properties;
12094        all_with_properties.extend(with_properties_after);
12095
12096        // For DYNAMIC/ICEBERG/EXTERNAL tables with columns, parse Snowflake-specific options
12097        // like TARGET_LAG, WAREHOUSE, CATALOG, EXTERNAL_VOLUME, LOCATION etc.
12098        if is_special_modifier {
12099            while !self.is_at_end()
12100                && !self.check(TokenType::As)
12101                && !self.check(TokenType::Semicolon)
12102            {
12103                // Check for known Snowflake table options (WAREHOUSE is a keyword, others are identifiers)
12104                // These are Snowflake-style options that use KEY=VALUE or KEY VALUE (without =)
12105                // Hive-style LOCATION/TBLPROPERTIES (without =) should NOT be matched here
12106                let is_snowflake_option = self.check(TokenType::Warehouse)
12107                    || self.check_identifier("TARGET_LAG")
12108                    || self.check_identifier("CATALOG")
12109                    || self.check_identifier("EXTERNAL_VOLUME")
12110                    || self.check_identifier("BASE_LOCATION")
12111                    || self.check_identifier("REFRESH_MODE")
12112                    || self.check_identifier("INITIALIZE")
12113                    || self.check_identifier("DATA_RETENTION_TIME_IN_DAYS")
12114                    || self.check_identifier("LOCATION")
12115                    || self.check_identifier("PARTITION")
12116                    || self.check_identifier("FILE_FORMAT")
12117                    || self.check_identifier("AUTO_REFRESH");
12118                if is_snowflake_option {
12119                    // Save position before consuming key - we might need to retreat for Hive-style syntax
12120                    let saved = self.current;
12121                    let key = self.advance().text;
12122                    if self.match_token(TokenType::Eq) {
12123                        // Capture value - could be string, identifier, stage path @..., keyword, or parenthesized options
12124                        let value = if self.check(TokenType::LParen) {
12125                            // Parenthesized option list like file_format = (type = parquet compression = gzip)
12126                            self.skip(); // consume (
12127                            let mut options = String::from("(");
12128                            let mut depth = 1;
12129                            while !self.is_at_end() && depth > 0 {
12130                                let tok = self.advance();
12131                                if tok.token_type == TokenType::LParen {
12132                                    depth += 1;
12133                                } else if tok.token_type == TokenType::RParen {
12134                                    depth -= 1;
12135                                }
12136                                // Add space before tokens that need it (not after open paren, not before close paren)
12137                                if !options.ends_with('(')
12138                                    && !options.ends_with(' ')
12139                                    && tok.token_type != TokenType::RParen
12140                                {
12141                                    options.push(' ');
12142                                }
12143                                options.push_str(&tok.text);
12144                            }
12145                            options
12146                        } else if self.check(TokenType::String) {
12147                            let v = format!("'{}'", self.peek().text);
12148                            self.skip();
12149                            v
12150                        } else if self.check(TokenType::DAt) {
12151                            // Stage path like @s1/logs/
12152                            self.skip(); // consume @
12153                            let mut path = String::from("@");
12154                            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
12155                                path.push_str(&self.advance().text);
12156                            }
12157                            // Parse path segments, but stop before Snowflake option keywords
12158                            while self.check(TokenType::Slash) {
12159                                // Peek ahead to see if next identifier is a Snowflake option keyword
12160                                if self.current + 1 < self.tokens.len() {
12161                                    let next = &self.tokens[self.current + 1];
12162                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12163                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12164                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12165                                        || next.text.eq_ignore_ascii_case("LOCATION")
12166                                        || next.text.eq_ignore_ascii_case("PARTITION")
12167                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12168                                    {
12169                                        // Consume the trailing slash before the keyword
12170                                        self.skip();
12171                                        path.push('/');
12172                                        break;
12173                                    }
12174                                }
12175                                self.skip();
12176                                path.push('/');
12177                                if self.is_identifier_token()
12178                                    || self.is_safe_keyword_as_identifier()
12179                                {
12180                                    path.push_str(&self.advance().text);
12181                                }
12182                            }
12183                            path
12184                        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
12185                            // Stage path tokenized as Var (e.g., @s2/logs/)
12186                            // When @ is followed by alphanumeric, tokenizer creates a Var token
12187                            let mut path = self.advance().text;
12188                            // Parse path segments, but stop before Snowflake option keywords
12189                            while self.check(TokenType::Slash) {
12190                                // Peek ahead to see if next identifier is a Snowflake option keyword
12191                                if self.current + 1 < self.tokens.len() {
12192                                    let next = &self.tokens[self.current + 1];
12193                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12194                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12195                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12196                                        || next.text.eq_ignore_ascii_case("LOCATION")
12197                                        || next.text.eq_ignore_ascii_case("PARTITION")
12198                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12199                                    {
12200                                        // Consume the trailing slash before the keyword
12201                                        self.skip();
12202                                        path.push('/');
12203                                        break;
12204                                    }
12205                                }
12206                                self.skip();
12207                                path.push('/');
12208                                if self.is_identifier_token()
12209                                    || self.is_safe_keyword_as_identifier()
12210                                {
12211                                    path.push_str(&self.advance().text);
12212                                }
12213                            }
12214                            path
12215                        } else if self.check(TokenType::Warehouse) {
12216                            self.advance().text
12217                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
12218                        {
12219                            self.advance().text
12220                        } else {
12221                            // No valid value after =, retreat and let Hive parsing try
12222                            self.current = saved;
12223                            break;
12224                        };
12225                        all_with_properties.push((key, value));
12226                    } else if self.is_identifier_token()
12227                        || self.is_safe_keyword_as_identifier()
12228                        || self.check(TokenType::Warehouse)
12229                    {
12230                        // WAREHOUSE mywh (without =)
12231                        let value = self.advance().text;
12232                        all_with_properties.push((key, value));
12233                    } else {
12234                        // Not a Snowflake-style option (e.g., Hive LOCATION 'path' without =)
12235                        // Retreat and let Hive parsing try
12236                        self.current = saved;
12237                        break;
12238                    }
12239                } else {
12240                    break;
12241                }
12242            }
12243        }
12244
12245        // Parse MySQL table options: ENGINE=val, AUTO_INCREMENT=val, DEFAULT CHARSET=val, etc.
12246        let mysql_table_options = if is_clickhouse {
12247            Vec::new()
12248        } else {
12249            self.parse_mysql_table_options()
12250        };
12251
12252        // Parse StarRocks ROLLUP property: ROLLUP (r1(col1, col2), r2(col1))
12253        let rollup = if self.match_token(TokenType::Rollup) {
12254            self.expect(TokenType::LParen)?;
12255            let mut indices = Vec::new();
12256            loop {
12257                let name = self.expect_identifier_or_keyword_with_quoted()?;
12258                let cols = if self.match_token(TokenType::LParen) {
12259                    let mut col_list = Vec::new();
12260                    loop {
12261                        col_list.push(self.expect_identifier_or_keyword_with_quoted()?);
12262                        if !self.match_token(TokenType::Comma) {
12263                            break;
12264                        }
12265                    }
12266                    self.expect(TokenType::RParen)?;
12267                    col_list
12268                } else {
12269                    Vec::new()
12270                };
12271                indices.push(crate::expressions::RollupIndex {
12272                    name,
12273                    expressions: cols,
12274                });
12275                if !self.match_token(TokenType::Comma) {
12276                    break;
12277                }
12278            }
12279            self.expect(TokenType::RParen)?;
12280            Some(crate::expressions::RollupProperty {
12281                expressions: indices,
12282            })
12283        } else {
12284            None
12285        };
12286
12287        // Parse Hive table properties: ROW FORMAT, STORED AS/BY, LOCATION, TBLPROPERTIES
12288        let hive_properties = self.parse_hive_table_properties()?;
12289        let is_teradata = matches!(
12290            self.config.dialect,
12291            Some(crate::dialects::DialectType::Teradata)
12292        );
12293
12294        // Handle ON COMMIT PRESERVE ROWS or ON COMMIT DELETE ROWS
12295        // Also handle TSQL ON filegroup or ON filegroup (partition_column)
12296        let (mut on_commit, on_property) = if is_teradata {
12297            (None, None)
12298        } else if self.match_token(TokenType::On) {
12299            if self.match_token(TokenType::Commit) {
12300                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
12301                    (Some(OnCommit::PreserveRows), None)
12302                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
12303                    (Some(OnCommit::DeleteRows), None)
12304                } else {
12305                    return Err(
12306                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
12307                    );
12308                }
12309            } else {
12310                // TSQL: ON filegroup or ON filegroup (partition_column)
12311                // Parse filegroup name as schema which allows filegroup(column) syntax
12312                let filegroup = self.parse_schema_identifier()?;
12313                (
12314                    None,
12315                    Some(OnProperty {
12316                        this: Box::new(filegroup),
12317                    }),
12318                )
12319            }
12320        } else {
12321            (None, None)
12322        };
12323
12324        // Parse table properties like DEFAULT COLLATE (BigQuery)
12325        let mut table_properties = hive_properties;
12326
12327        // If COMMENT was found before WITH, add it to table_properties as SchemaCommentProperty
12328        if let Some(comment_text) = pre_with_comment {
12329            table_properties.push(Expression::SchemaCommentProperty(Box::new(
12330                SchemaCommentProperty {
12331                    this: Box::new(Expression::Literal(Box::new(Literal::String(comment_text)))),
12332                },
12333            )));
12334        }
12335
12336        if self.match_token(TokenType::Default) && self.match_token(TokenType::Collate) {
12337            let collation = self.parse_primary()?;
12338            table_properties.push(Expression::CollateProperty(Box::new(CollateProperty {
12339                this: Box::new(collation),
12340                default: Some(Box::new(Expression::Boolean(BooleanLiteral {
12341                    value: true,
12342                }))),
12343            })));
12344        }
12345
12346        // BigQuery: OPTIONS (key=value, ...) on table - comes after column definitions
12347        if matches!(
12348            self.config.dialect,
12349            Some(crate::dialects::DialectType::BigQuery)
12350        ) {
12351            if let Some(options_property) = self.parse_bigquery_options_property()? {
12352                table_properties.push(options_property);
12353            }
12354        } else if self.match_identifier("OPTIONS") {
12355            let options = self.parse_options_list()?;
12356            table_properties.push(Expression::Properties(Box::new(Properties {
12357                expressions: options,
12358            })));
12359        }
12360
12361        // Doris/StarRocks: PROPERTIES ('key'='value', ...) - comes after column definitions
12362        let is_doris_starrocks = matches!(
12363            self.config.dialect,
12364            Some(crate::dialects::DialectType::Doris)
12365                | Some(crate::dialects::DialectType::StarRocks)
12366        );
12367        if is_doris_starrocks && self.match_identifier("PROPERTIES") {
12368            // Use parse_options_list which handles 'key'='value' format
12369            let props = self.parse_options_list()?;
12370            if !props.is_empty() {
12371                table_properties.push(Expression::Properties(Box::new(Properties {
12372                    expressions: props,
12373                })));
12374            }
12375        }
12376
12377        // Redshift: Parse DISTKEY, SORTKEY, DISTSTYLE, BACKUP after column definitions
12378        // These can appear in any order and multiple times
12379        loop {
12380            if self.match_identifier("DISTKEY") {
12381                // DISTKEY(column)
12382                if let Some(distkey) = self.parse_distkey()? {
12383                    table_properties.push(distkey);
12384                }
12385            } else if self.match_text_seq(&["COMPOUND", "SORTKEY"]) {
12386                // COMPOUND SORTKEY(col1, col2, ...)
12387                if let Some(sortkey) = self.parse_sortkey()? {
12388                    // Set compound flag
12389                    if let Expression::SortKeyProperty(mut skp) = sortkey {
12390                        skp.compound = Some(Box::new(Expression::Boolean(BooleanLiteral {
12391                            value: true,
12392                        })));
12393                        table_properties.push(Expression::SortKeyProperty(skp));
12394                    }
12395                }
12396            } else if self.match_identifier("SORTKEY") {
12397                // SORTKEY(col1, col2, ...)
12398                if let Some(sortkey) = self.parse_sortkey()? {
12399                    table_properties.push(sortkey);
12400                }
12401            } else if self.match_identifier("DISTSTYLE") {
12402                // DISTSTYLE ALL|EVEN|AUTO|KEY
12403                if self.match_texts(&["ALL", "EVEN", "AUTO", "KEY"]) {
12404                    let style = self.previous().text.to_ascii_uppercase();
12405                    table_properties.push(Expression::DistStyleProperty(Box::new(
12406                        DistStyleProperty {
12407                            this: Box::new(Expression::Var(Box::new(Var { this: style }))),
12408                        },
12409                    )));
12410                }
12411            } else if self.match_identifier("BACKUP") {
12412                // BACKUP YES|NO
12413                if self.match_texts(&["YES", "NO"]) {
12414                    let value = self.previous().text.to_ascii_uppercase();
12415                    table_properties.push(Expression::BackupProperty(Box::new(BackupProperty {
12416                        this: Box::new(Expression::Var(Box::new(Var { this: value }))),
12417                    })));
12418                }
12419            } else {
12420                break;
12421            }
12422        }
12423
12424        // Teradata: PRIMARY/UNIQUE/INDEX and PARTITION BY clauses after columns
12425        if is_teradata {
12426            loop {
12427                // Consume optional comma separator between index specs (only if followed by an index keyword)
12428                if self.check(TokenType::Comma) {
12429                    let saved_comma = self.current;
12430                    self.skip(); // consume comma
12431                    let is_index_keyword = self.check(TokenType::Unique)
12432                        || self.check(TokenType::PrimaryKey)
12433                        || self.check(TokenType::Index)
12434                        || self.check(TokenType::No);
12435                    if !is_index_keyword {
12436                        self.current = saved_comma; // retreat
12437                    }
12438                }
12439                if self.match_token(TokenType::Unique) {
12440                    let primary = self.match_token(TokenType::PrimaryKey);
12441                    let amp = self.match_identifier("AMP");
12442                    self.match_token(TokenType::Index);
12443                    let params = if self.match_token(TokenType::LParen) {
12444                        let cols = self.parse_identifier_list()?;
12445                        self.expect(TokenType::RParen)?;
12446                        cols.into_iter()
12447                            .map(|id| {
12448                                Expression::boxed_column(Column {
12449                                    name: id,
12450                                    table: None,
12451                                    join_mark: false,
12452                                    trailing_comments: Vec::new(),
12453                                    span: None,
12454                                    inferred_type: None,
12455                                })
12456                            })
12457                            .collect()
12458                    } else {
12459                        Vec::new()
12460                    };
12461                    table_properties.push(Expression::Index(Box::new(Index {
12462                        this: None,
12463                        table: None,
12464                        unique: true,
12465                        primary: if primary {
12466                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12467                                value: true,
12468                            })))
12469                        } else {
12470                            None
12471                        },
12472                        amp: if amp {
12473                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12474                                value: true,
12475                            })))
12476                        } else {
12477                            None
12478                        },
12479                        params,
12480                    })));
12481                    continue;
12482                }
12483                if self.match_token(TokenType::PrimaryKey) {
12484                    let amp = self.match_identifier("AMP");
12485                    self.match_token(TokenType::Index);
12486                    let params = if self.match_token(TokenType::LParen) {
12487                        let cols = self.parse_identifier_list()?;
12488                        self.expect(TokenType::RParen)?;
12489                        cols.into_iter()
12490                            .map(|id| {
12491                                Expression::boxed_column(Column {
12492                                    name: id,
12493                                    table: None,
12494                                    join_mark: false,
12495                                    trailing_comments: Vec::new(),
12496                                    span: None,
12497                                    inferred_type: None,
12498                                })
12499                            })
12500                            .collect()
12501                    } else {
12502                        Vec::new()
12503                    };
12504                    table_properties.push(Expression::Index(Box::new(Index {
12505                        this: None,
12506                        table: None,
12507                        unique: false,
12508                        primary: Some(Box::new(Expression::Boolean(BooleanLiteral {
12509                            value: true,
12510                        }))),
12511                        amp: if amp {
12512                            Some(Box::new(Expression::Boolean(BooleanLiteral {
12513                                value: true,
12514                            })))
12515                        } else {
12516                            None
12517                        },
12518                        params,
12519                    })));
12520                    continue;
12521                }
12522                if self.match_token(TokenType::Index) {
12523                    let params = if self.match_token(TokenType::LParen) {
12524                        let cols = self.parse_identifier_list()?;
12525                        self.expect(TokenType::RParen)?;
12526                        cols.into_iter()
12527                            .map(|id| {
12528                                Expression::boxed_column(Column {
12529                                    name: id,
12530                                    table: None,
12531                                    join_mark: false,
12532                                    trailing_comments: Vec::new(),
12533                                    span: None,
12534                                    inferred_type: None,
12535                                })
12536                            })
12537                            .collect()
12538                    } else {
12539                        Vec::new()
12540                    };
12541                    table_properties.push(Expression::Index(Box::new(Index {
12542                        this: None,
12543                        table: None,
12544                        unique: false,
12545                        primary: None,
12546                        amp: None,
12547                        params,
12548                    })));
12549                    continue;
12550                }
12551                if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
12552                    let expr = self.parse_primary()?;
12553                    table_properties.push(Expression::PartitionedByProperty(Box::new(
12554                        PartitionedByProperty {
12555                            this: Box::new(expr),
12556                        },
12557                    )));
12558                    continue;
12559                }
12560                break;
12561            }
12562
12563            if on_commit.is_none()
12564                && self.check(TokenType::On)
12565                && self.check_next(TokenType::Commit)
12566            {
12567                self.skip(); // ON
12568                self.skip(); // COMMIT
12569                if self.match_keywords(&[TokenType::Preserve, TokenType::Rows]) {
12570                    on_commit = Some(OnCommit::PreserveRows);
12571                } else if self.match_keywords(&[TokenType::Delete, TokenType::Rows]) {
12572                    on_commit = Some(OnCommit::DeleteRows);
12573                } else {
12574                    return Err(
12575                        self.parse_error("Expected PRESERVE ROWS or DELETE ROWS after ON COMMIT")
12576                    );
12577                }
12578            }
12579        }
12580
12581        // ClickHouse: table properties after column definitions
12582        if is_clickhouse {
12583            self.parse_clickhouse_table_properties(&mut table_properties)?;
12584        }
12585
12586        // ClickHouse: EMPTY AS SELECT
12587        if matches!(
12588            self.config.dialect,
12589            Some(crate::dialects::DialectType::ClickHouse)
12590        ) && self.match_identifier("EMPTY")
12591        {
12592            table_properties.push(Expression::Var(Box::new(Var {
12593                this: "EMPTY".to_string(),
12594            })));
12595        }
12596
12597        // Handle AS SELECT after columns/WITH (CTAS with column definitions)
12598        // When there are no column definitions, AS comes after PARTITION BY/CLUSTER BY/OPTIONS
12599        let as_select = if !no_column_defs && self.match_token(TokenType::As) {
12600            Some(self.parse_statement()?)
12601        } else {
12602            None
12603        };
12604
12605        if is_clickhouse && as_select.is_some() {
12606            self.parse_clickhouse_table_properties(&mut table_properties)?;
12607        }
12608
12609        // Parse PARTITION BY RANGE/LIST/HASH(columns) for regular CREATE TABLE
12610        let is_bigquery = matches!(
12611            self.config.dialect,
12612            Some(crate::dialects::DialectType::BigQuery)
12613        );
12614        if !is_teradata && (self.check(TokenType::Partition) || self.check(TokenType::PartitionBy))
12615        {
12616            let parsed_bigquery_partition = if is_bigquery {
12617                if let Some(partition_property) = self.parse_bigquery_partition_by_property()? {
12618                    table_properties.push(partition_property);
12619                    true
12620                } else {
12621                    false
12622                }
12623            } else {
12624                false
12625            };
12626
12627            if !parsed_bigquery_partition {
12628                let saved = self.current;
12629                let is_partition_by = if self.match_token(TokenType::PartitionBy) {
12630                    true
12631                } else if self.match_token(TokenType::Partition) {
12632                    self.match_token(TokenType::By)
12633                } else {
12634                    false
12635                };
12636                if is_partition_by {
12637                    let partition_kind = if self.check(TokenType::Range) {
12638                        self.skip();
12639                        Some("RANGE".to_string())
12640                    } else if self.check(TokenType::List) {
12641                        self.skip();
12642                        Some("LIST".to_string())
12643                    } else if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
12644                        && self.check_next(TokenType::LParen)
12645                    {
12646                        // Only treat identifier as partition method (like HASH) if followed by (
12647                        Some(self.advance().text.to_ascii_uppercase())
12648                    } else {
12649                        // No explicit partition method (RANGE/LIST/HASH), just PARTITION BY (cols)
12650                        None
12651                    };
12652
12653                    // StarRocks/Doris: PARTITION BY func(), col (bare expressions without RANGE/LIST)
12654                    // When the partition_kind was consumed as an identifier that's actually a function call
12655                    // and the content after the parenthesized args includes a comma, it's a bare expression list
12656                    if is_doris_starrocks
12657                        && partition_kind.is_some()
12658                        && !matches!(
12659                            partition_kind.as_deref(),
12660                            Some("RANGE") | Some("LIST") | Some("HASH") | Some("KEY")
12661                        )
12662                    {
12663                        // Backtrack: re-parse as bare PARTITION BY with comma-separated expressions
12664                        let func_name = partition_kind.unwrap();
12665                        let mut raw_sql = format!("PARTITION BY {}", func_name);
12666                        // Helper closure for consuming parenthesized content with proper spacing
12667                        fn consume_parens(parser: &mut Parser, raw_sql: &mut String) {
12668                            if !parser.check(TokenType::LParen) {
12669                                return;
12670                            }
12671                            parser.advance();
12672                            raw_sql.push('(');
12673                            let mut depth = 1;
12674                            let mut last_type: Option<TokenType> = None;
12675                            while !parser.is_at_end() && depth > 0 {
12676                                let tok = parser.advance();
12677                                if tok.token_type == TokenType::LParen {
12678                                    depth += 1;
12679                                } else if tok.token_type == TokenType::RParen {
12680                                    depth -= 1;
12681                                    if depth == 0 {
12682                                        break;
12683                                    }
12684                                }
12685                                // Add space after commas
12686                                if matches!(last_type, Some(TokenType::Comma)) {
12687                                    raw_sql.push(' ');
12688                                }
12689                                if tok.token_type == TokenType::String {
12690                                    raw_sql.push('\'');
12691                                    raw_sql.push_str(&tok.text);
12692                                    raw_sql.push('\'');
12693                                } else {
12694                                    raw_sql.push_str(&tok.text);
12695                                }
12696                                last_type = Some(tok.token_type.clone());
12697                            }
12698                            raw_sql.push(')');
12699                        }
12700                        consume_parens(self, &mut raw_sql);
12701                        // Consume more comma-separated expressions
12702                        while self.match_token(TokenType::Comma) {
12703                            raw_sql.push_str(", ");
12704                            let tok = self.advance();
12705                            raw_sql.push_str(&tok.text);
12706                            consume_parens(self, &mut raw_sql);
12707                        }
12708                        table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
12709                    } else
12710                    // For Doris/StarRocks/MySQL RANGE/LIST, use structured parsing
12711                    if (is_doris_starrocks
12712                        || matches!(
12713                            self.config.dialect,
12714                            Some(crate::dialects::DialectType::MySQL)
12715                                | Some(crate::dialects::DialectType::SingleStore)
12716                                | Some(crate::dialects::DialectType::TiDB)
12717                        ))
12718                        && matches!(partition_kind.as_deref(), Some("RANGE") | Some("LIST"))
12719                    {
12720                        let partition_expr = self.parse_doris_partition_by_range_or_list(
12721                            partition_kind
12722                                .as_ref()
12723                                .map(|s| s.as_str())
12724                                .unwrap_or("RANGE"),
12725                        )?;
12726                        table_properties.push(partition_expr);
12727                    } else {
12728                        // Generic raw SQL parsing for other dialects
12729                        let no_partition_kind = partition_kind.is_none();
12730                        let mut raw_sql = match partition_kind {
12731                            Some(kind) => format!("PARTITION BY {}", kind),
12732                            None => "PARTITION BY ".to_string(),
12733                        };
12734                        if self.check(TokenType::LParen) {
12735                            self.skip();
12736                            raw_sql.push('(');
12737                            let mut depth = 1;
12738                            let mut last_tok_type: Option<TokenType> = None;
12739                            while !self.is_at_end() && depth > 0 {
12740                                let tok = self.advance();
12741                                if tok.token_type == TokenType::LParen {
12742                                    depth += 1;
12743                                } else if tok.token_type == TokenType::RParen {
12744                                    depth -= 1;
12745                                    if depth == 0 {
12746                                        break;
12747                                    }
12748                                }
12749                                // Add space before token if needed for proper formatting
12750                                let needs_space = match (&last_tok_type, &tok.token_type) {
12751                                    // Add space after comma
12752                                    (Some(TokenType::Comma), _) => true,
12753                                    // Add space after identifiers/keywords before other identifiers/keywords
12754                                    (Some(TokenType::Identifier), TokenType::Identifier) => true,
12755                                    _ => false,
12756                                };
12757                                if needs_space {
12758                                    raw_sql.push(' ');
12759                                }
12760                                // Handle string literals - preserve quotes
12761                                if tok.token_type == TokenType::String {
12762                                    raw_sql.push('\'');
12763                                    raw_sql.push_str(&tok.text);
12764                                    raw_sql.push('\'');
12765                                } else {
12766                                    raw_sql.push_str(&tok.text);
12767                                }
12768                                last_tok_type = Some(tok.token_type.clone());
12769                            }
12770                            raw_sql.push(')');
12771                        } else if no_partition_kind {
12772                            // Bare PARTITION BY expression list without a partition method
12773                            let mut first = true;
12774                            while !self.is_at_end()
12775                                && !self.check(TokenType::Cluster)
12776                                && !self.check(TokenType::As)
12777                                && !self.check(TokenType::Semicolon)
12778                                && !self.check(TokenType::RParen)
12779                                && !self.check_identifier("OPTIONS")
12780                            {
12781                                if !first {
12782                                    raw_sql.push_str(", ");
12783                                }
12784                                first = false;
12785                                let tok = self.advance();
12786                                raw_sql.push_str(&tok.text);
12787                                // Handle function calls: PARTITION BY DATE(col)
12788                                if self.check(TokenType::LParen) {
12789                                    self.skip();
12790                                    raw_sql.push('(');
12791                                    let mut depth = 1;
12792                                    while !self.is_at_end() && depth > 0 {
12793                                        let t = self.advance();
12794                                        if t.token_type == TokenType::LParen {
12795                                            depth += 1;
12796                                        } else if t.token_type == TokenType::RParen {
12797                                            depth -= 1;
12798                                            if depth == 0 {
12799                                                break;
12800                                            }
12801                                        }
12802                                        raw_sql.push_str(&t.text);
12803                                    }
12804                                    raw_sql.push(')');
12805                                }
12806                                if !self.match_token(TokenType::Comma) {
12807                                    break;
12808                                }
12809                            }
12810                        }
12811                        table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
12812                    }
12813                } else {
12814                    self.current = saved;
12815                }
12816            }
12817        }
12818
12819        // Parse CLUSTER BY (BigQuery) after PARTITION BY
12820        if is_bigquery {
12821            if let Some(cluster_property) = self.parse_bigquery_cluster_by_property()? {
12822                table_properties.push(cluster_property);
12823            }
12824        } else if self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
12825            let mut cluster_names = Vec::new();
12826            loop {
12827                let name = self.expect_identifier_or_keyword()?;
12828                cluster_names.push(name);
12829                if !self.match_token(TokenType::Comma) {
12830                    break;
12831                }
12832            }
12833            table_properties.push(Expression::Raw(Raw {
12834                sql: format!("CLUSTER BY {}", cluster_names.join(", ")),
12835            }));
12836        }
12837
12838        // No-column-defs path: OPTIONS and AS SELECT come after PARTITION BY / CLUSTER BY
12839        if no_column_defs {
12840            if matches!(
12841                self.config.dialect,
12842                Some(crate::dialects::DialectType::BigQuery)
12843            ) {
12844                if let Some(options_property) = self.parse_bigquery_options_property()? {
12845                    table_properties.push(options_property);
12846                }
12847            } else if self.match_identifier("OPTIONS") {
12848                let options = self.parse_options_list()?;
12849                table_properties.push(Expression::Properties(Box::new(Properties {
12850                    expressions: options,
12851                })));
12852            }
12853        }
12854
12855        let as_select = if no_column_defs && self.match_token(TokenType::As) {
12856            Some(self.parse_statement()?)
12857        } else {
12858            as_select
12859        };
12860
12861        // For EXTERNAL tables, parse additional Snowflake options that may come after PARTITION BY
12862        // (location=@s2/logs/, partition_type = user_specified, file_format = (...), etc.)
12863        if is_special_modifier {
12864            while !self.is_at_end()
12865                && !self.check(TokenType::As)
12866                && !self.check(TokenType::Semicolon)
12867            {
12868                let is_snowflake_option = self.check(TokenType::Warehouse)
12869                    || self.check_identifier("TARGET_LAG")
12870                    || self.check_identifier("CATALOG")
12871                    || self.check_identifier("EXTERNAL_VOLUME")
12872                    || self.check_identifier("BASE_LOCATION")
12873                    || self.check_identifier("REFRESH_MODE")
12874                    || self.check_identifier("INITIALIZE")
12875                    || self.check_identifier("DATA_RETENTION_TIME_IN_DAYS")
12876                    || self.check_identifier("LOCATION")
12877                    || self.check_identifier("PARTITION_TYPE")
12878                    || self.check_identifier("FILE_FORMAT")
12879                    || self.check_identifier("AUTO_REFRESH");
12880                if is_snowflake_option {
12881                    let key = self.advance().text;
12882                    if self.match_token(TokenType::Eq) {
12883                        let value = if self.check(TokenType::LParen) {
12884                            // Parenthesized option list
12885                            self.skip();
12886                            let mut options = String::from("(");
12887                            let mut depth = 1;
12888                            while !self.is_at_end() && depth > 0 {
12889                                let tok = self.advance();
12890                                if tok.token_type == TokenType::LParen {
12891                                    depth += 1;
12892                                } else if tok.token_type == TokenType::RParen {
12893                                    depth -= 1;
12894                                }
12895                                if !options.ends_with('(')
12896                                    && !options.ends_with(' ')
12897                                    && tok.token_type != TokenType::RParen
12898                                {
12899                                    options.push(' ');
12900                                }
12901                                options.push_str(&tok.text);
12902                            }
12903                            options
12904                        } else if self.check(TokenType::String) {
12905                            let v = format!("'{}'", self.peek().text);
12906                            self.skip();
12907                            v
12908                        } else if self.check(TokenType::DAt) {
12909                            // Stage path like @s1/logs/
12910                            self.skip();
12911                            let mut path = String::from("@");
12912                            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
12913                                path.push_str(&self.advance().text);
12914                            }
12915                            while self.check(TokenType::Slash) {
12916                                if self.current + 1 < self.tokens.len() {
12917                                    let next = &self.tokens[self.current + 1];
12918                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12919                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12920                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12921                                        || next.text.eq_ignore_ascii_case("LOCATION")
12922                                        || next.text.eq_ignore_ascii_case("PARTITION")
12923                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12924                                    {
12925                                        self.skip();
12926                                        path.push('/');
12927                                        break;
12928                                    }
12929                                }
12930                                self.skip();
12931                                path.push('/');
12932                                if self.is_identifier_token()
12933                                    || self.is_safe_keyword_as_identifier()
12934                                {
12935                                    path.push_str(&self.advance().text);
12936                                }
12937                            }
12938                            path
12939                        } else if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
12940                            let mut path = self.advance().text;
12941                            while self.check(TokenType::Slash) {
12942                                if self.current + 1 < self.tokens.len() {
12943                                    let next = &self.tokens[self.current + 1];
12944                                    if next.text.eq_ignore_ascii_case("FILE_FORMAT")
12945                                        || next.text.eq_ignore_ascii_case("PARTITION_TYPE")
12946                                        || next.text.eq_ignore_ascii_case("AUTO_REFRESH")
12947                                        || next.text.eq_ignore_ascii_case("LOCATION")
12948                                        || next.text.eq_ignore_ascii_case("PARTITION")
12949                                        || next.text.eq_ignore_ascii_case("WAREHOUSE")
12950                                    {
12951                                        self.skip();
12952                                        path.push('/');
12953                                        break;
12954                                    }
12955                                }
12956                                self.skip();
12957                                path.push('/');
12958                                if self.is_identifier_token()
12959                                    || self.is_safe_keyword_as_identifier()
12960                                {
12961                                    path.push_str(&self.advance().text);
12962                                }
12963                            }
12964                            path
12965                        } else if self.check(TokenType::Warehouse) {
12966                            self.advance().text
12967                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
12968                        {
12969                            self.advance().text
12970                        } else {
12971                            break;
12972                        };
12973                        all_with_properties.push((key, value));
12974                    } else if self.is_identifier_token()
12975                        || self.is_safe_keyword_as_identifier()
12976                        || self.check(TokenType::Warehouse)
12977                    {
12978                        let value = self.advance().text;
12979                        all_with_properties.push((key, value));
12980                    }
12981                } else {
12982                    break;
12983                }
12984            }
12985        }
12986
12987        // Parse TSQL table-level WITH(SYSTEM_VERSIONING=ON(...)) after columns
12988        // This is different from the earlier WITH properties parsing.
12989        // TSQL uses WITH(...) after columns for system versioning.
12990        let post_table_properties = self.parse_post_table_properties()?;
12991
12992        // PostgreSQL: INHERITS (parent1, parent2, ...)
12993        let inherits = if self.match_identifier("INHERITS") {
12994            self.expect(TokenType::LParen)?;
12995            let mut parents = Vec::new();
12996            loop {
12997                parents.push(self.parse_table_ref()?);
12998                if !self.match_token(TokenType::Comma) {
12999                    break;
13000                }
13001            }
13002            self.expect(TokenType::RParen)?;
13003            parents
13004        } else {
13005            Vec::new()
13006        };
13007
13008        Ok(Expression::CreateTable(Box::new(CreateTable {
13009            name,
13010            on_cluster,
13011            columns,
13012            constraints,
13013            if_not_exists,
13014            temporary,
13015            or_replace,
13016            table_modifier: table_modifier.map(|s| s.to_string()),
13017            as_select,
13018            as_select_parenthesized: false,
13019            on_commit,
13020            clone_source: None,
13021            clone_at_clause: None,
13022            shallow_clone: false,
13023            is_copy: false,
13024            leading_comments,
13025            with_properties: all_with_properties,
13026            teradata_post_name_options: teradata_post_name_options.clone(),
13027            with_data: None,
13028            with_statistics: None,
13029            teradata_indexes: Vec::new(),
13030            with_cte: None,
13031            properties: table_properties,
13032            partition_of: None,
13033            post_table_properties,
13034            mysql_table_options,
13035            inherits,
13036            on_property,
13037            copy_grants,
13038            using_template: None,
13039            rollup,
13040            uuid,
13041        })))
13042    }
13043
13044    /// Parse CREATE TABLE ... PARTITION OF parent_table [(cols)] [FOR VALUES spec | DEFAULT] [PARTITION BY ...]
13045    fn parse_create_table_partition_of(
13046        &mut self,
13047        name: TableRef,
13048        if_not_exists: bool,
13049        temporary: bool,
13050        or_replace: bool,
13051        table_modifier: Option<&str>,
13052        leading_comments: Vec<String>,
13053    ) -> Result<Expression> {
13054        // Parse parent table name
13055        let parent_table = self.parse_table_ref()?;
13056
13057        // Optionally parse column constraints in parens: (unitsales DEFAULT 0) or (CONSTRAINT ...)
13058        // This must come before FOR VALUES or DEFAULT. We distinguish from other uses
13059        // by checking if the first token after LParen is CONSTRAINT or an identifier
13060        // that is not a string literal.
13061        let (columns, constraints) = if self.check(TokenType::LParen) {
13062            // Peek ahead: current is LParen, current+1 is first token inside parens
13063            let first_inside = self.current + 1;
13064            // Check if this is a partition column specification: (colname DEFAULT value)
13065            // Column names tokenize as Var (unquoted) or QuotedIdentifier (quoted)
13066            let is_column_defs = first_inside < self.tokens.len()
13067                && (self.tokens[first_inside].token_type == TokenType::Constraint
13068                    || ((self.tokens[first_inside].token_type == TokenType::Var
13069                        || self.tokens[first_inside].token_type == TokenType::QuotedIdentifier
13070                        || self.tokens[first_inside].token_type == TokenType::Identifier)
13071                        && first_inside + 1 < self.tokens.len()
13072                        && self.tokens[first_inside + 1].token_type == TokenType::Default));
13073
13074            if is_column_defs {
13075                self.skip(); // consume LParen
13076                             // Use special parsing for partition column specs - they don't have data types,
13077                             // just column names with constraint overrides like DEFAULT
13078                let (cols, constrs) = self.parse_partition_column_specs()?;
13079                self.expect(TokenType::RParen)?;
13080                (cols, constrs)
13081            } else {
13082                (Vec::new(), Vec::new())
13083            }
13084        } else {
13085            (Vec::new(), Vec::new())
13086        };
13087
13088        // Parse DEFAULT or FOR VALUES spec
13089        let partition_bound: Expression = if self.match_token(TokenType::Default) {
13090            // DEFAULT partition
13091            Expression::Var(Box::new(Var {
13092                this: "DEFAULT".to_string(),
13093            }))
13094        } else if self.match_token(TokenType::For) {
13095            // FOR VALUES ...
13096            self.expect(TokenType::Values)?;
13097            self.parse_partition_bound_spec()?
13098        } else {
13099            // Neither DEFAULT nor FOR VALUES - could be an error
13100            // but we'll be lenient and just create a DEFAULT
13101            Expression::Var(Box::new(Var {
13102                this: "DEFAULT".to_string(),
13103            }))
13104        };
13105
13106        let partition_of_expr =
13107            Expression::PartitionedOfProperty(Box::new(PartitionedOfProperty {
13108                this: Box::new(Expression::Table(Box::new(parent_table))),
13109                expression: Box::new(partition_bound),
13110            }));
13111
13112        // Optionally parse trailing PARTITION BY RANGE/LIST/HASH(columns)
13113        let mut table_properties: Vec<Expression> = Vec::new();
13114        if self.match_token(TokenType::Partition) || self.match_token(TokenType::PartitionBy) {
13115            // Could be PARTITION BY or just PartitionBy token
13116            if self.previous().token_type == TokenType::Partition {
13117                self.expect(TokenType::By)?;
13118            }
13119            // Parse RANGE/LIST/HASH(columns)
13120            let partition_kind = if self.check(TokenType::Identifier) || self.check(TokenType::Var)
13121            {
13122                let kind_text = self.advance().text.to_ascii_uppercase();
13123                kind_text
13124            } else if self.check(TokenType::Range) {
13125                self.skip();
13126                "RANGE".to_string()
13127            } else if self.check(TokenType::List) {
13128                self.skip();
13129                "LIST".to_string()
13130            } else {
13131                "RANGE".to_string()
13132            };
13133            // Parse (columns)
13134            let mut raw_sql = format!("PARTITION BY {}", partition_kind);
13135            if self.check(TokenType::LParen) {
13136                self.skip(); // consume LParen
13137                raw_sql.push('(');
13138                let mut depth = 1;
13139                while !self.is_at_end() && depth > 0 {
13140                    let tok = self.advance();
13141                    if tok.token_type == TokenType::LParen {
13142                        depth += 1;
13143                    } else if tok.token_type == TokenType::RParen {
13144                        depth -= 1;
13145                        if depth == 0 {
13146                            break;
13147                        }
13148                    }
13149                    raw_sql.push_str(&tok.text);
13150                }
13151                raw_sql.push(')');
13152            }
13153            table_properties.push(Expression::Raw(Raw { sql: raw_sql }));
13154        }
13155
13156        Ok(Expression::CreateTable(Box::new(CreateTable {
13157            name,
13158            on_cluster: None,
13159            columns,
13160            constraints,
13161            if_not_exists,
13162            temporary,
13163            or_replace,
13164            table_modifier: table_modifier.map(|s| s.to_string()),
13165            as_select: None,
13166            as_select_parenthesized: false,
13167            on_commit: None,
13168            clone_source: None,
13169            clone_at_clause: None,
13170            shallow_clone: false,
13171            is_copy: false,
13172            leading_comments,
13173            with_properties: Vec::new(),
13174            teradata_post_name_options: Vec::new(),
13175            with_data: None,
13176            with_statistics: None,
13177            teradata_indexes: Vec::new(),
13178            with_cte: None,
13179            properties: table_properties,
13180            partition_of: Some(partition_of_expr),
13181            post_table_properties: Vec::new(),
13182            mysql_table_options: Vec::new(),
13183            inherits: Vec::new(),
13184            on_property: None,
13185            copy_grants: false,
13186            using_template: None,
13187            rollup: None,
13188            uuid: None,
13189        })))
13190    }
13191
13192    /// Parse partition bound spec for PARTITION OF: IN (...), FROM (...) TO (...), or WITH (MODULUS n, REMAINDER n)
13193    fn parse_partition_bound_spec(&mut self) -> Result<Expression> {
13194        if self.match_token(TokenType::In) {
13195            // IN (val, val, ...)
13196            self.expect(TokenType::LParen)?;
13197            let mut values = Vec::new();
13198            loop {
13199                let val = self.parse_expression()?;
13200                values.push(val);
13201                if !self.match_token(TokenType::Comma) {
13202                    break;
13203                }
13204            }
13205            self.expect(TokenType::RParen)?;
13206            // Use Tuple for multiple values (generator strips parens for partition bounds)
13207            let this_expr = if values.len() == 1 {
13208                values.into_iter().next().unwrap()
13209            } else {
13210                Expression::Tuple(Box::new(Tuple {
13211                    expressions: values,
13212                }))
13213            };
13214            Ok(Expression::PartitionBoundSpec(Box::new(
13215                PartitionBoundSpec {
13216                    this: Some(Box::new(this_expr)),
13217                    expression: None,
13218                    from_expressions: None,
13219                    to_expressions: None,
13220                },
13221            )))
13222        } else if self.match_token(TokenType::From) {
13223            // FROM (val, ...) TO (val, ...)
13224            self.expect(TokenType::LParen)?;
13225            let mut from_vals = Vec::new();
13226            loop {
13227                let val = self.parse_partition_bound_value()?;
13228                from_vals.push(val);
13229                if !self.match_token(TokenType::Comma) {
13230                    break;
13231                }
13232            }
13233            self.expect(TokenType::RParen)?;
13234
13235            self.expect(TokenType::To)?;
13236            self.expect(TokenType::LParen)?;
13237            let mut to_vals = Vec::new();
13238            loop {
13239                let val = self.parse_partition_bound_value()?;
13240                to_vals.push(val);
13241                if !self.match_token(TokenType::Comma) {
13242                    break;
13243                }
13244            }
13245            self.expect(TokenType::RParen)?;
13246
13247            let from_expr = if from_vals.len() == 1 {
13248                from_vals.into_iter().next().unwrap()
13249            } else {
13250                Expression::Tuple(Box::new(Tuple {
13251                    expressions: from_vals,
13252                }))
13253            };
13254            let to_expr = if to_vals.len() == 1 {
13255                to_vals.into_iter().next().unwrap()
13256            } else {
13257                Expression::Tuple(Box::new(Tuple {
13258                    expressions: to_vals,
13259                }))
13260            };
13261
13262            Ok(Expression::PartitionBoundSpec(Box::new(
13263                PartitionBoundSpec {
13264                    this: None,
13265                    expression: None,
13266                    from_expressions: Some(Box::new(from_expr)),
13267                    to_expressions: Some(Box::new(to_expr)),
13268                },
13269            )))
13270        } else if self.match_token(TokenType::With) {
13271            // WITH (MODULUS n, REMAINDER n)
13272            self.expect(TokenType::LParen)?;
13273            self.match_text_seq(&["MODULUS"]);
13274            let modulus = self.parse_expression()?;
13275            self.expect(TokenType::Comma)?;
13276            self.match_text_seq(&["REMAINDER"]);
13277            let remainder = self.parse_expression()?;
13278            self.expect(TokenType::RParen)?;
13279
13280            Ok(Expression::PartitionBoundSpec(Box::new(
13281                PartitionBoundSpec {
13282                    this: Some(Box::new(modulus)),
13283                    expression: Some(Box::new(remainder)),
13284                    from_expressions: None,
13285                    to_expressions: None,
13286                },
13287            )))
13288        } else {
13289            Err(self.parse_error("Expected IN, FROM, or WITH after FOR VALUES in PARTITION OF"))
13290        }
13291    }
13292
13293    /// Parse a single partition bound value (number, string, MINVALUE, MAXVALUE)
13294    fn parse_partition_bound_value(&mut self) -> Result<Expression> {
13295        if self.match_token(TokenType::Minvalue) {
13296            Ok(Expression::Var(Box::new(Var {
13297                this: "MINVALUE".to_string(),
13298            })))
13299        } else if self.match_token(TokenType::Maxvalue) {
13300            Ok(Expression::Var(Box::new(Var {
13301                this: "MAXVALUE".to_string(),
13302            })))
13303        } else {
13304            self.parse_expression()
13305        }
13306    }
13307
13308    /// Parse column specifications for PostgreSQL PARTITION OF syntax.
13309    /// Unlike regular column definitions, these don't have data types - just column names
13310    /// with constraint overrides like DEFAULT, NOT NULL, or table-level CONSTRAINT clauses.
13311    /// Example: (unitsales DEFAULT 0) or (CONSTRAINT check_date CHECK (logdate >= '2016-07-01'))
13312    fn parse_partition_column_specs(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>)> {
13313        let mut columns = Vec::new();
13314        let mut constraints = Vec::new();
13315
13316        loop {
13317            // Check for table-level constraint (CONSTRAINT name ...)
13318            if self.check(TokenType::Constraint) {
13319                constraints.push(self.parse_table_constraint()?);
13320            } else if self.check(TokenType::PrimaryKey)
13321                || self.check(TokenType::ForeignKey)
13322                || self.check(TokenType::Unique)
13323                || self.check(TokenType::Check)
13324                || self.check(TokenType::Exclude)
13325            {
13326                constraints.push(self.parse_table_constraint()?);
13327            } else {
13328                // Parse column name with optional constraints (no data type)
13329                columns.push(self.parse_partition_column_spec()?);
13330            }
13331
13332            if !self.match_token(TokenType::Comma) {
13333                break;
13334            }
13335            // ClickHouse allows a trailing comma before the closing ')'
13336            if matches!(
13337                self.config.dialect,
13338                Some(crate::dialects::DialectType::ClickHouse)
13339            ) && self.check(TokenType::RParen)
13340            {
13341                break;
13342            }
13343        }
13344
13345        Ok((columns, constraints))
13346    }
13347
13348    /// Parse a single partition column specification: column_name [DEFAULT value] [NOT NULL] [NULL] [WITH OPTIONS ...]
13349    fn parse_partition_column_spec(&mut self) -> Result<ColumnDef> {
13350        // Parse column name
13351        let name = self.expect_identifier_or_safe_keyword_with_quoted()?;
13352
13353        // Create column def with Unknown data type (data type comes from parent table)
13354        let mut col_def = ColumnDef::new(name.name.clone(), DataType::Unknown);
13355        col_def.name = name;
13356
13357        // Parse column constraints (no data type expected)
13358        loop {
13359            if self.match_token(TokenType::Default) {
13360                // DEFAULT value
13361                let default_val = self.parse_expression()?;
13362                col_def.default = Some(default_val.clone());
13363                col_def
13364                    .constraints
13365                    .push(ColumnConstraint::Default(default_val));
13366                col_def.constraint_order.push(ConstraintType::Default);
13367            } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13368                col_def.nullable = Some(false);
13369                col_def.constraint_order.push(ConstraintType::NotNull);
13370            } else if self.match_token(TokenType::Null) {
13371                col_def.nullable = Some(true);
13372                col_def.constraint_order.push(ConstraintType::Null);
13373            } else if self.match_token(TokenType::Constraint) {
13374                // Inline CONSTRAINT name ... for this column
13375                let constraint_name = self.expect_identifier_or_safe_keyword()?;
13376                if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13377                    col_def.nullable = Some(false);
13378                    col_def.not_null_constraint_name = Some(constraint_name);
13379                    col_def.constraint_order.push(ConstraintType::NotNull);
13380                } else if self.match_token(TokenType::Check) {
13381                    col_def.check_constraint_name = Some(constraint_name);
13382                    if self.match_token(TokenType::LParen) {
13383                        let check_expr = self.parse_expression()?;
13384                        self.expect(TokenType::RParen)?;
13385                        col_def
13386                            .constraints
13387                            .push(ColumnConstraint::Check(check_expr));
13388                    }
13389                    col_def.constraint_order.push(ConstraintType::Check);
13390                } else if self.match_token(TokenType::Default) {
13391                    let default_val = self.parse_expression()?;
13392                    col_def.default = Some(default_val.clone());
13393                    col_def
13394                        .constraints
13395                        .push(ColumnConstraint::Default(default_val));
13396                    col_def.constraint_order.push(ConstraintType::Default);
13397                }
13398            } else if self.match_text_seq(&["WITH", "OPTIONS"]) {
13399                // PostgreSQL: WITH OPTIONS allows specifying more options
13400                // For now, just skip this - it's rarely used
13401                break;
13402            } else {
13403                break;
13404            }
13405        }
13406
13407        Ok(col_def)
13408    }
13409
13410    /// Parse WITH properties for CREATE TABLE (e.g., WITH (FORMAT='parquet', x='2'))
13411    /// Returns a list of (key, value) pairs
13412    fn parse_with_properties(&mut self) -> Result<Vec<(String, String)>> {
13413        self.expect(TokenType::LParen)?;
13414        let mut properties = Vec::new();
13415
13416        loop {
13417            if self.check(TokenType::RParen) {
13418                break;
13419            }
13420
13421            // Parse property name (can be keywords like FORMAT, TABLE_FORMAT)
13422            let mut key = self.expect_identifier_or_keyword()?;
13423
13424            // Handle multi-word keys like "PARTITIONED BY" -> "PARTITIONED_BY"
13425            if key.eq_ignore_ascii_case("PARTITIONED") && self.check(TokenType::By) {
13426                self.skip(); // consume BY
13427                key = "PARTITIONED_BY".to_string();
13428            }
13429
13430            // Expect = or special case for PARTITIONED_BY=(...)
13431            self.expect(TokenType::Eq)?;
13432
13433            // Parse property value - can be string, identifier, or parenthesized expression
13434            let value = if self.check(TokenType::String) {
13435                // Store string with quotes to preserve format
13436                let val = format!("'{}'", self.peek().text);
13437                self.skip();
13438                val
13439            } else if self.match_token(TokenType::LParen) {
13440                // Handle PARTITIONED_BY=(x INT, y INT) or similar
13441                let mut depth = 1;
13442                let mut result = String::from("(");
13443                let mut need_space = false;
13444                while !self.is_at_end() && depth > 0 {
13445                    if self.check(TokenType::LParen) {
13446                        depth += 1;
13447                    } else if self.check(TokenType::RParen) {
13448                        depth -= 1;
13449                        if depth == 0 {
13450                            break;
13451                        }
13452                    }
13453                    let token = self.peek();
13454                    let text = &token.text;
13455                    let token_type = token.token_type;
13456
13457                    // Determine if we need a space before this token
13458                    let is_punctuation = matches!(
13459                        token_type,
13460                        TokenType::Comma | TokenType::LParen | TokenType::RParen
13461                    );
13462                    if need_space && !is_punctuation {
13463                        result.push(' ');
13464                    }
13465
13466                    result.push_str(text);
13467
13468                    // Determine if we need a space after this token
13469                    need_space = token_type == TokenType::Comma
13470                        || (!is_punctuation
13471                            && !matches!(
13472                                token_type,
13473                                TokenType::LParen | TokenType::RParen | TokenType::Comma
13474                            ));
13475                    self.skip();
13476                }
13477                self.expect(TokenType::RParen)?;
13478                result.push(')');
13479                result
13480            } else if self.check_identifier("ARRAY")
13481                && self
13482                    .peek_nth(1)
13483                    .is_some_and(|t| t.token_type == TokenType::LBracket)
13484            {
13485                // Handle ARRAY['value', 'value', ...] syntax (Athena/Presto)
13486                let mut result = self.advance().text.clone(); // consume ARRAY
13487                self.expect(TokenType::LBracket)?;
13488                result.push('[');
13489                let mut first = true;
13490                while !self.is_at_end() && !self.check(TokenType::RBracket) {
13491                    if !first {
13492                        if self.match_token(TokenType::Comma) {
13493                            result.push_str(", ");
13494                        } else {
13495                            break;
13496                        }
13497                    }
13498                    first = false;
13499                    // Parse array element (usually a string)
13500                    if self.check(TokenType::String) {
13501                        result.push('\'');
13502                        result.push_str(&self.advance().text);
13503                        result.push('\'');
13504                    } else if self.is_identifier_token() {
13505                        result.push_str(&self.advance().text);
13506                    } else {
13507                        break;
13508                    }
13509                }
13510                self.expect(TokenType::RBracket)?;
13511                result.push(']');
13512                result
13513            } else if self.check(TokenType::Number) {
13514                // Numeric value (e.g., bucket_count=64)
13515                self.advance().text.clone()
13516            } else {
13517                // Just an identifier or keyword (e.g., allow_page_locks=on)
13518                self.expect_identifier_or_keyword()?
13519            };
13520
13521            properties.push((key, value));
13522
13523            if !self.match_token(TokenType::Comma) {
13524                break;
13525            }
13526        }
13527
13528        self.expect(TokenType::RParen)?;
13529        Ok(properties)
13530    }
13531
13532    /// Parse column definitions and table constraints
13533    fn parse_column_definitions(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>)> {
13534        let mut columns = Vec::new();
13535        let mut constraints = Vec::new();
13536
13537        loop {
13538            if self.check(TokenType::RParen) {
13539                break;
13540            }
13541            // Check for LIKE clause (PostgreSQL)
13542            if self.check(TokenType::Like) {
13543                constraints.push(self.parse_like_clause()?);
13544            }
13545            // Check for table-level constraint
13546            // For CHECK, only treat as constraint if followed by '(' (NOT in ClickHouse — there
13547            // CHECK/ASSUME without CONSTRAINT keyword is not supported, and 'check' can be a column name).
13548            // Otherwise, 'check' is a column name (e.g., CREATE TABLE t (check INT)).
13549            else if self.check(TokenType::Constraint)
13550                || self.check(TokenType::PrimaryKey)
13551                || self.check(TokenType::ForeignKey)
13552                || self.check(TokenType::Unique)
13553                || (self.check(TokenType::Check)
13554                    && !matches!(
13555                        self.config.dialect,
13556                        Some(crate::dialects::DialectType::ClickHouse)
13557                    )
13558                    && self
13559                        .peek_nth(1)
13560                        .map_or(false, |t| t.token_type == TokenType::LParen))
13561                || self.check(TokenType::Exclude)
13562            {
13563                constraints.push(self.parse_table_constraint()?);
13564            } else if matches!(
13565                self.config.dialect,
13566                Some(crate::dialects::DialectType::ClickHouse)
13567            ) && self.check(TokenType::Index)
13568            {
13569                // ClickHouse: INDEX name expr TYPE type_func(args) GRANULARITY n
13570                self.skip(); // consume INDEX
13571                let name = self.expect_identifier_or_keyword_with_quoted()?;
13572                // Use parse_conjunction to handle comparisons like c0 < (SELECT _table)
13573                let expression = self.parse_conjunction()?.ok_or_else(|| {
13574                    self.parse_error("Expected expression in ClickHouse INDEX definition")
13575                })?;
13576                let index_type = if self.match_token(TokenType::Type) {
13577                    // Parse function or identifier for type (e.g., bloom_filter(0.001), set(100), minmax)
13578                    // Handle keywords like 'set' that are tokenized as TokenType::Set
13579                    if let Some(func) = self.parse_function()? {
13580                        Some(Box::new(func))
13581                    } else if !self.check(TokenType::Identifier)
13582                        && !self.check(TokenType::Var)
13583                        && !self.is_at_end()
13584                    {
13585                        // Handle keywords as index type names (e.g., set, minmax)
13586                        let type_name = self.advance().text.clone();
13587                        if self.check(TokenType::LParen) {
13588                            // It's a function call like set(100)
13589                            self.skip(); // consume (
13590                            let mut args = Vec::new();
13591                            if !self.check(TokenType::RParen) {
13592                                args.push(self.parse_expression()?);
13593                                while self.match_token(TokenType::Comma) {
13594                                    args.push(self.parse_expression()?);
13595                                }
13596                            }
13597                            self.expect(TokenType::RParen)?;
13598                            Some(Box::new(Expression::Function(Box::new(Function::new(
13599                                type_name, args,
13600                            )))))
13601                        } else {
13602                            // Just an identifier
13603                            Some(Box::new(Expression::Identifier(Identifier::new(type_name))))
13604                        }
13605                    } else if let Some(id) = self.parse_id_var()? {
13606                        Some(Box::new(id))
13607                    } else {
13608                        None
13609                    }
13610                } else {
13611                    None
13612                };
13613                let granularity = if self.match_identifier("GRANULARITY") {
13614                    let gran_val = self.parse_expression()?;
13615                    Some(Box::new(gran_val))
13616                } else {
13617                    None
13618                };
13619                constraints.push(TableConstraint::Index {
13620                    name: Some(name),
13621                    columns: Vec::new(),
13622                    kind: None,
13623                    modifiers: ConstraintModifiers::default(),
13624                    use_key_keyword: false,
13625                    expression: Some(Box::new(expression)),
13626                    index_type,
13627                    granularity,
13628                });
13629            } else if !matches!(
13630                self.config.dialect,
13631                Some(crate::dialects::DialectType::ClickHouse)
13632            ) && (self.check(TokenType::Index)
13633                || self.check(TokenType::Key)
13634                || self.check_identifier("FULLTEXT")
13635                || self.check_identifier("SPATIAL"))
13636            {
13637                // INDEX/KEY constraint (MySQL). Guard KEY <type> as a normal column definition
13638                // (e.g. ClickHouse: `key UInt64`).
13639                let looks_like_key_constraint = if self.check(TokenType::Key) {
13640                    self.check_next(TokenType::LParen)
13641                        || ((self.check_next(TokenType::Identifier)
13642                            || self.check_next(TokenType::Var)
13643                            || self.check_next(TokenType::QuotedIdentifier))
13644                            && self.current + 2 < self.tokens.len()
13645                            && self.tokens[self.current + 2].token_type == TokenType::LParen)
13646                } else {
13647                    true
13648                };
13649
13650                if looks_like_key_constraint {
13651                    constraints.push(self.parse_index_table_constraint()?);
13652                } else {
13653                    columns.push(self.parse_column_def()?);
13654                }
13655            } else if self.check_identifier("PERIOD") {
13656                // TSQL: PERIOD FOR SYSTEM_TIME (start_col, end_col)
13657                if let Some(period_constraint) =
13658                    self.parse_period_for_system_time_table_constraint()?
13659                {
13660                    constraints.push(period_constraint);
13661                } else {
13662                    // Not actually PERIOD FOR SYSTEM_TIME, treat as column definition
13663                    columns.push(self.parse_column_def()?);
13664                }
13665            } else if self.check_identifier("INITIALLY") {
13666                // PostgreSQL: INITIALLY DEFERRED / INITIALLY IMMEDIATE as table-level setting
13667                self.skip(); // consume INITIALLY
13668                if self.match_identifier("DEFERRED") {
13669                    constraints.push(TableConstraint::InitiallyDeferred { deferred: true });
13670                } else if self.match_identifier("IMMEDIATE") {
13671                    constraints.push(TableConstraint::InitiallyDeferred { deferred: false });
13672                } else {
13673                    return Err(self.parse_error("Expected DEFERRED or IMMEDIATE after INITIALLY"));
13674                }
13675            } else if matches!(
13676                self.config.dialect,
13677                Some(crate::dialects::DialectType::ClickHouse)
13678            ) && self.check_identifier("PROJECTION")
13679            {
13680                // ClickHouse: PROJECTION name (SELECT ...) or PROJECTION name INDEX expr TYPE type_name
13681                self.skip(); // consume PROJECTION
13682                let name = self.expect_identifier_or_keyword_with_quoted()?;
13683                if self.match_token(TokenType::LParen) {
13684                    let expression = self.parse_statement()?;
13685                    self.expect(TokenType::RParen)?;
13686                    // ClickHouse: PROJECTION name (SELECT ...) WITH SETTINGS (key=value, ...)
13687                    if self.check(TokenType::With)
13688                        && self.current + 1 < self.tokens.len()
13689                        && self.tokens[self.current + 1].token_type == TokenType::Settings
13690                    {
13691                        self.skip(); // consume WITH
13692                        self.skip(); // consume SETTINGS
13693                        if self.match_token(TokenType::LParen) {
13694                            // Consume key=value pairs
13695                            loop {
13696                                if self.check(TokenType::RParen) {
13697                                    break;
13698                                }
13699                                if self.is_identifier_token()
13700                                    || self.is_safe_keyword_as_identifier()
13701                                {
13702                                    self.skip(); // key
13703                                }
13704                                if self.match_token(TokenType::Eq) {
13705                                    let _ = self.parse_primary()?; // value
13706                                }
13707                                if !self.match_token(TokenType::Comma) {
13708                                    break;
13709                                }
13710                            }
13711                            self.expect(TokenType::RParen)?;
13712                        }
13713                    }
13714                    constraints.push(TableConstraint::Projection { name, expression });
13715                } else if self.match_token(TokenType::Index) {
13716                    // PROJECTION name INDEX expr TYPE type_name
13717                    let expr = self.parse_bitwise()?.ok_or_else(|| {
13718                        self.parse_error(
13719                            "Expected expression in ClickHouse PROJECTION INDEX definition",
13720                        )
13721                    })?;
13722                    let type_str = if self.match_token(TokenType::Type) {
13723                        if !self.is_at_end()
13724                            && !self.check(TokenType::Comma)
13725                            && !self.check(TokenType::RParen)
13726                        {
13727                            self.advance().text.clone()
13728                        } else {
13729                            String::new()
13730                        }
13731                    } else {
13732                        String::new()
13733                    };
13734                    let raw_sql = if type_str.is_empty() {
13735                        format!("INDEX {} ", expr)
13736                    } else {
13737                        format!("INDEX {} TYPE {}", expr, type_str)
13738                    };
13739                    constraints.push(TableConstraint::Projection {
13740                        name,
13741                        expression: Expression::Raw(Raw { sql: raw_sql }),
13742                    });
13743                } else {
13744                    constraints.push(TableConstraint::Projection {
13745                        name,
13746                        expression: Expression::Null(Null),
13747                    });
13748                }
13749            } else {
13750                // Parse column definition
13751                columns.push(self.parse_column_def()?);
13752            }
13753
13754            if !self.match_token(TokenType::Comma) {
13755                break;
13756            }
13757            // ClickHouse: allow trailing comma before closing paren
13758            if matches!(
13759                self.config.dialect,
13760                Some(crate::dialects::DialectType::ClickHouse)
13761            ) && self.check(TokenType::RParen)
13762            {
13763                break;
13764            }
13765        }
13766
13767        Ok((columns, constraints))
13768    }
13769
13770    /// Parse LIKE clause in CREATE TABLE: LIKE source_table [INCLUDING|EXCLUDING options]
13771    fn parse_like_clause(&mut self) -> Result<TableConstraint> {
13772        self.expect(TokenType::Like)?;
13773        let source = self.parse_table_ref()?;
13774        let mut options = Vec::new();
13775
13776        // Parse optional INCLUDING/EXCLUDING modifiers
13777        loop {
13778            if self.match_identifier("INCLUDING") {
13779                let prop = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
13780                options.push((LikeOptionAction::Including, prop));
13781            } else if self.match_identifier("EXCLUDING") {
13782                let prop = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
13783                options.push((LikeOptionAction::Excluding, prop));
13784            } else {
13785                break;
13786            }
13787        }
13788
13789        Ok(TableConstraint::Like { source, options })
13790    }
13791
13792    /// Parse a single column definition
13793    fn parse_column_def(&mut self) -> Result<ColumnDef> {
13794        // Column names can be keywords like 'end', 'truncate', 'view', etc.
13795        // ClickHouse allows any keyword as column name (from, select, etc.)
13796        let mut name = if matches!(
13797            self.config.dialect,
13798            Some(crate::dialects::DialectType::ClickHouse)
13799        ) {
13800            self.expect_identifier_or_keyword_with_quoted()?
13801        } else {
13802            self.expect_identifier_or_safe_keyword_with_quoted()?
13803        };
13804        // ClickHouse: Nested column names like n.b for Nested() columns
13805        if matches!(
13806            self.config.dialect,
13807            Some(crate::dialects::DialectType::ClickHouse)
13808        ) {
13809            while self.match_token(TokenType::Dot) {
13810                let sub = self.expect_identifier_or_safe_keyword_with_quoted()?;
13811                name = Identifier {
13812                    name: format!("{}.{}", name.name, sub.name),
13813                    quoted: name.quoted,
13814                    trailing_comments: sub.trailing_comments,
13815                    span: None,
13816                };
13817            }
13818        }
13819
13820        // TSQL computed columns have no data type: column_name AS (expression) [PERSISTED]
13821        // Check if AS follows immediately (no data type)
13822        if self.check(TokenType::As) {
13823            let mut col_def = ColumnDef::new(
13824                name.name.clone(),
13825                DataType::Custom {
13826                    name: String::new(),
13827                },
13828            );
13829            col_def.name = name;
13830            // Consume AS and parse computed column expression
13831            self.skip(); // consume AS
13832            if self.check(TokenType::LParen) {
13833                self.parse_as_computed_column(&mut col_def)?;
13834            }
13835            return Ok(col_def);
13836        }
13837
13838        // SQLite allows column definitions without types: CREATE TABLE t (x, y)
13839        // ClickHouse allows typeless columns with DEFAULT/MATERIALIZED/ALIAS/EPHEMERAL
13840        // Check if the next token indicates no type (comma, rparen, or constraint keyword)
13841        let no_type = self.check(TokenType::Comma)
13842            || self.check(TokenType::RParen)
13843            || (matches!(
13844                self.config.dialect,
13845                Some(crate::dialects::DialectType::ClickHouse)
13846            ) && (self.check(TokenType::Default)
13847                || self.check(TokenType::Materialized)
13848                || self.check_identifier("ALIAS")
13849                || self.check_identifier("EPHEMERAL")));
13850        let data_type = if no_type {
13851            // No type specified - use empty custom type
13852            DataType::Custom {
13853                name: String::new(),
13854            }
13855        } else {
13856            self.parse_data_type()?
13857        };
13858
13859        let mut col_def = ColumnDef::new(name.name.clone(), data_type);
13860        col_def.name = name;
13861        col_def.no_type = no_type;
13862
13863        // Parse MySQL type modifiers (UNSIGNED, ZEROFILL)
13864        // These come after the data type but before other constraints
13865        while self.match_identifier("UNSIGNED")
13866            || self.match_identifier("ZEROFILL")
13867            || self.match_identifier("SIGNED")
13868        {
13869            let modifier = self.previous().text.to_ascii_uppercase();
13870            if modifier == "UNSIGNED" {
13871                col_def.unsigned = true;
13872            } else if modifier == "ZEROFILL" {
13873                col_def.zerofill = true;
13874            }
13875            // SIGNED is the default, no action needed
13876        }
13877
13878        // BigQuery: OPTIONS (key=value, ...) on column - comes right after type
13879        if self.match_identifier("OPTIONS") {
13880            col_def.options = self.parse_options_list()?;
13881        }
13882
13883        // Parse column constraints
13884        loop {
13885            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13886                col_def.nullable = Some(false);
13887                col_def.constraint_order.push(ConstraintType::NotNull);
13888            } else if self.match_token(TokenType::Null) {
13889                col_def.nullable = Some(true);
13890                col_def.constraint_order.push(ConstraintType::Null);
13891            } else if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
13892                // Handle PRIMARY KEY [ASC|DESC]
13893                col_def.primary_key = true;
13894                // Capture ASC/DESC after PRIMARY KEY
13895                if self.match_token(TokenType::Asc) {
13896                    col_def.primary_key_order = Some(SortOrder::Asc);
13897                } else if self.match_token(TokenType::Desc) {
13898                    col_def.primary_key_order = Some(SortOrder::Desc);
13899                }
13900                col_def.constraint_order.push(ConstraintType::PrimaryKey);
13901            } else if self.match_token(TokenType::Constraint) {
13902                // Inline CONSTRAINT name ... (e.g., CONSTRAINT fk_name REFERENCES ...)
13903                let constraint_name = self.expect_identifier()?;
13904                // After constraint name, expect REFERENCES, PRIMARY KEY, UNIQUE, CHECK, NOT NULL, NULL, etc.
13905                if self.match_token(TokenType::References) {
13906                    let mut fk_ref = self.parse_foreign_key_ref()?;
13907                    fk_ref.constraint_name = Some(constraint_name);
13908                    col_def
13909                        .constraints
13910                        .push(ColumnConstraint::References(fk_ref));
13911                    col_def.constraint_order.push(ConstraintType::References);
13912                } else if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
13913                    col_def.primary_key = true;
13914                    col_def.primary_key_constraint_name = Some(constraint_name);
13915                    col_def.constraint_order.push(ConstraintType::PrimaryKey);
13916                } else if self.match_token(TokenType::Unique) {
13917                    col_def.unique = true;
13918                    col_def.unique_constraint_name = Some(constraint_name);
13919                    // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
13920                    if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
13921                        col_def.unique_nulls_not_distinct = true;
13922                    }
13923                    col_def.constraint_order.push(ConstraintType::Unique);
13924                } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
13925                    col_def.nullable = Some(false);
13926                    col_def.not_null_constraint_name = Some(constraint_name);
13927                    col_def.constraint_order.push(ConstraintType::NotNull);
13928                } else if self.match_token(TokenType::Check) {
13929                    col_def.check_constraint_name = Some(constraint_name);
13930                    // Parse CHECK constraint expression
13931                    if self.match_token(TokenType::LParen) {
13932                        let check_expr = self.parse_expression()?;
13933                        self.expect(TokenType::RParen)?;
13934                        col_def
13935                            .constraints
13936                            .push(ColumnConstraint::Check(check_expr));
13937                    } else if matches!(
13938                        self.config.dialect,
13939                        Some(crate::dialects::DialectType::ClickHouse)
13940                    ) {
13941                        // ClickHouse: CHECK expr without parens
13942                        let check_expr = self.parse_or()?;
13943                        col_def
13944                            .constraints
13945                            .push(ColumnConstraint::Check(check_expr));
13946                    }
13947                    col_def.constraint_order.push(ConstraintType::Check);
13948                }
13949            } else if self.match_token(TokenType::Unique) {
13950                col_def.unique = true;
13951                // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
13952                if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
13953                    col_def.unique_nulls_not_distinct = true;
13954                }
13955                col_def.constraint_order.push(ConstraintType::Unique);
13956            } else if self.match_token(TokenType::Check) {
13957                // Standalone CHECK (expr) constraint (without CONSTRAINT name)
13958                if self.match_token(TokenType::LParen) {
13959                    let check_expr = self.parse_expression()?;
13960                    self.expect(TokenType::RParen)?;
13961                    col_def
13962                        .constraints
13963                        .push(ColumnConstraint::Check(check_expr));
13964                    col_def.constraint_order.push(ConstraintType::Check);
13965                } else if matches!(
13966                    self.config.dialect,
13967                    Some(crate::dialects::DialectType::ClickHouse)
13968                ) {
13969                    // ClickHouse: CHECK expr without parens
13970                    let check_expr = self.parse_or()?;
13971                    col_def
13972                        .constraints
13973                        .push(ColumnConstraint::Check(check_expr));
13974                    col_def.constraint_order.push(ConstraintType::Check);
13975                }
13976            } else if self.match_token(TokenType::AutoIncrement) || self.match_keyword("IDENTITY") {
13977                col_def.auto_increment = true;
13978                col_def.constraint_order.push(ConstraintType::AutoIncrement);
13979                // Handle IDENTITY/AUTOINCREMENT options: START n INCREMENT m [ORDER|NOORDER] or (start, increment)
13980                if self.match_keyword("START") {
13981                    col_def.auto_increment_start = Some(Box::new(self.parse_primary()?));
13982                    if self.match_keyword("INCREMENT") {
13983                        col_def.auto_increment_increment = Some(Box::new(self.parse_primary()?));
13984                    }
13985                    // Snowflake: ORDER or NOORDER option
13986                    if self.match_token(TokenType::Order) {
13987                        col_def.auto_increment_order = Some(true);
13988                    } else if self.match_identifier("NOORDER") {
13989                        col_def.auto_increment_order = Some(false);
13990                    }
13991                } else if self.match_token(TokenType::LParen) {
13992                    // IDENTITY(start, increment) or AUTOINCREMENT(start, increment)
13993                    col_def.auto_increment_start = Some(Box::new(self.parse_primary()?));
13994                    if self.match_token(TokenType::Comma) {
13995                        col_def.auto_increment_increment = Some(Box::new(self.parse_primary()?));
13996                    }
13997                    self.expect(TokenType::RParen)?;
13998                }
13999            } else if self.match_token(TokenType::Default) {
14000                // ClickHouse: DEFAULT expressions can be complex (today(), a + 1, cond ? x : y, etc.)
14001                col_def.default = if matches!(
14002                    self.config.dialect,
14003                    Some(crate::dialects::DialectType::ClickHouse)
14004                ) {
14005                    Some(self.parse_expression()?)
14006                } else {
14007                    Some(self.parse_unary()?)
14008                };
14009                col_def.constraint_order.push(ConstraintType::Default);
14010            } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
14011                // Snowflake/SQL Server: FOREIGN KEY REFERENCES table(columns)
14012                // The FOREIGN KEY keywords are followed by REFERENCES
14013                self.expect(TokenType::References)?;
14014                let mut fk_ref = self.parse_foreign_key_ref()?;
14015                fk_ref.has_foreign_key_keywords = true;
14016                col_def
14017                    .constraints
14018                    .push(ColumnConstraint::References(fk_ref));
14019                col_def.constraint_order.push(ConstraintType::References);
14020            } else if self.match_token(TokenType::References) {
14021                let fk_ref = self.parse_foreign_key_ref()?;
14022                col_def
14023                    .constraints
14024                    .push(ColumnConstraint::References(fk_ref));
14025                col_def.constraint_order.push(ConstraintType::References);
14026            } else if self.match_token(TokenType::Generated) {
14027                // GENERATED [BY DEFAULT [ON NULL] | ALWAYS] AS ...
14028                // Could be: AS IDENTITY, AS (expr) STORED|VIRTUAL, AS ROW START|END
14029                self.parse_generated_column_constraint(&mut col_def)?;
14030            } else if self.match_token(TokenType::Collate) {
14031                // COLLATE collation_name (may be quoted like "de_DE")
14032                // Also handle dotted names like pg_catalog."default"
14033                let mut collation = self.expect_identifier_or_keyword_with_quoted()?;
14034                // Check for dotted collation names: pg_catalog."default"
14035                while self.match_token(TokenType::Dot) {
14036                    let next = self.expect_identifier_or_keyword_with_quoted()?;
14037                    let sep = if next.quoted {
14038                        format!("{}.\"{}\"", collation.name, next.name)
14039                    } else {
14040                        format!("{}.{}", collation.name, next.name)
14041                    };
14042                    collation = Identifier {
14043                        name: sep,
14044                        quoted: false,
14045                        trailing_comments: Vec::new(),
14046                        span: None,
14047                    };
14048                }
14049                col_def
14050                    .constraints
14051                    .push(ColumnConstraint::Collate(collation));
14052                col_def.constraint_order.push(ConstraintType::Collate);
14053            } else if self.match_token(TokenType::Comment) {
14054                // COMMENT 'comment text'
14055                let comment_text = self.expect_string()?;
14056                col_def
14057                    .constraints
14058                    .push(ColumnConstraint::Comment(comment_text));
14059                col_def.constraint_order.push(ConstraintType::Comment);
14060            } else if self.match_keywords(&[TokenType::On, TokenType::Update]) {
14061                // MySQL: ON UPDATE expression (e.g., ON UPDATE CURRENT_TIMESTAMP)
14062                let expr = self.parse_unary()?;
14063                col_def.on_update = Some(expr);
14064                col_def.constraint_order.push(ConstraintType::OnUpdate);
14065            } else if self.match_identifier("ENCODE") {
14066                // Redshift: ENCODE encoding_type (e.g., ZSTD, DELTA, LZO, etc.)
14067                let encoding = self.expect_identifier_or_keyword()?;
14068                col_def.encoding = Some(encoding);
14069                col_def.constraint_order.push(ConstraintType::Encode);
14070            } else if !matches!(
14071                self.config.dialect,
14072                Some(crate::dialects::DialectType::ClickHouse)
14073            ) && self.match_token(TokenType::Format)
14074            {
14075                // Teradata: FORMAT 'pattern' (not ClickHouse — FORMAT there is statement-level)
14076                let format_str = self.expect_string()?;
14077                col_def.format = Some(format_str);
14078            } else if self.match_identifier("TITLE") {
14079                // Teradata: TITLE 'title'
14080                let title_str = self.expect_string()?;
14081                col_def.title = Some(title_str);
14082            } else if self.match_identifier("INLINE") {
14083                // Teradata: INLINE LENGTH n
14084                self.match_identifier("LENGTH");
14085                let length = self.expect_number()?;
14086                col_def.inline_length = Some(length as u64);
14087            } else if self.match_identifier("COMPRESS") {
14088                // Teradata: COMPRESS or COMPRESS (values) or COMPRESS 'value'
14089                if self.match_token(TokenType::LParen) {
14090                    let values = self.parse_expression_list()?;
14091                    self.expect(TokenType::RParen)?;
14092                    col_def.compress = Some(values);
14093                } else if self.check(TokenType::String) {
14094                    // COMPRESS 'value'
14095                    let value = self.parse_primary()?;
14096                    col_def.compress = Some(vec![value]);
14097                } else {
14098                    // COMPRESS without values
14099                    col_def.compress = Some(Vec::new());
14100                }
14101            } else if self.match_identifier("CHARACTER") {
14102                // Teradata: CHARACTER SET name
14103                self.match_token(TokenType::Set);
14104                let charset = self.expect_identifier_or_keyword()?;
14105                col_def.character_set = Some(charset);
14106            } else if self.match_identifier("UPPERCASE") {
14107                // Teradata: UPPERCASE
14108                col_def.uppercase = true;
14109            } else if self.match_identifier("CASESPECIFIC") {
14110                // Teradata: CASESPECIFIC
14111                col_def.casespecific = Some(true);
14112            } else if self.match_text_seq(&["NOT", "FOR", "REPLICATION"]) {
14113                // TSQL: NOT FOR REPLICATION - skip this modifier (not preserved in output for non-TSQL)
14114                col_def.not_for_replication = true;
14115            } else if self.match_token(TokenType::Not) && self.match_identifier("CASESPECIFIC") {
14116                // Teradata: NOT CASESPECIFIC
14117                col_def.casespecific = Some(false);
14118            } else if self.match_keyword("TAG")
14119                || (self.match_token(TokenType::With) && self.match_keyword("TAG"))
14120            {
14121                // Snowflake: TAG (key='value', ...) or WITH TAG (key='value', ...)
14122                let tags = self.parse_tags()?;
14123                col_def.constraints.push(ColumnConstraint::Tags(tags));
14124                col_def.constraint_order.push(ConstraintType::Tags);
14125            } else if self.match_token(TokenType::As) {
14126                // Computed column: AS (expression) [STORED|VIRTUAL|PERSISTED] [NOT NULL]
14127                // TSQL: AS (expression) [PERSISTED] [NOT NULL]
14128                // MySQL shorthand: AS (expression) [STORED|VIRTUAL]
14129                // Also: Snowflake External Table virtual column expression
14130                if self.check(TokenType::LParen) {
14131                    self.parse_as_computed_column(&mut col_def)?;
14132                }
14133            } else if self.match_identifier("CODEC") {
14134                // ClickHouse: CODEC(LZ4HC(9), ZSTD, DELTA)
14135                self.expect(TokenType::LParen)?;
14136                let start = self.current;
14137                let mut depth = 1;
14138                while !self.is_at_end() && depth > 0 {
14139                    if self.check(TokenType::LParen) {
14140                        depth += 1;
14141                    }
14142                    if self.check(TokenType::RParen) {
14143                        depth -= 1;
14144                        if depth == 0 {
14145                            break;
14146                        }
14147                    }
14148                    self.skip();
14149                }
14150                let codec_text = self.tokens_to_sql(start, self.current);
14151                self.expect(TokenType::RParen)?;
14152                col_def.codec = Some(codec_text);
14153            } else if self.match_identifier("STATISTICS") {
14154                // ClickHouse: STATISTICS(tdigest, minmax, uniq, ...)
14155                self.expect(TokenType::LParen)?;
14156                let mut depth = 1;
14157                while !self.is_at_end() && depth > 0 {
14158                    if self.check(TokenType::LParen) {
14159                        depth += 1;
14160                    }
14161                    if self.check(TokenType::RParen) {
14162                        depth -= 1;
14163                        if depth == 0 {
14164                            break;
14165                        }
14166                    }
14167                    self.skip();
14168                }
14169                self.expect(TokenType::RParen)?;
14170                // Statistics info is stored but we don't need it for transpilation
14171            } else if self.match_identifier("EPHEMERAL") {
14172                // ClickHouse: EPHEMERAL [expr] [type]
14173                // EPHEMERAL can optionally be followed by an expression, then optionally a data type
14174                if !self.check(TokenType::Comma)
14175                    && !self.check(TokenType::RParen)
14176                    && !self.is_at_end()
14177                    && !self.check_identifier("CODEC")
14178                    && !self.check_identifier("TTL")
14179                    && !self.check(TokenType::Comment)
14180                {
14181                    let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
14182                    col_def.ephemeral = Some(Some(Box::new(expr)));
14183                    // ClickHouse: type can follow EPHEMERAL expression (e.g., b EPHEMERAL 'a' String)
14184                    if col_def.no_type
14185                        && !self.check(TokenType::Comma)
14186                        && !self.check(TokenType::RParen)
14187                        && !self.is_at_end()
14188                        && !self.check_identifier("CODEC")
14189                        && !self.check_identifier("TTL")
14190                        && !self.check(TokenType::Comment)
14191                    {
14192                        col_def.data_type = self.parse_data_type()?;
14193                        col_def.no_type = false;
14194                    }
14195                } else {
14196                    col_def.ephemeral = Some(None);
14197                }
14198            } else if self.check(TokenType::Materialized) && !self.check_next(TokenType::View) {
14199                // ClickHouse: MATERIALIZED expr (but not MATERIALIZED VIEW)
14200                self.skip(); // consume MATERIALIZED
14201                let expr = self.parse_or()?;
14202                col_def.materialized_expr = Some(Box::new(expr));
14203            } else if self.match_identifier("ALIAS") {
14204                // ClickHouse: ALIAS expr
14205                let expr = self.parse_or()?;
14206                col_def.alias_expr = Some(Box::new(expr));
14207            } else if matches!(
14208                self.config.dialect,
14209                Some(crate::dialects::DialectType::ClickHouse)
14210            ) && self.check_identifier("EXPRESSION")
14211            {
14212                // ClickHouse dictionary column: EXPRESSION expr
14213                self.skip(); // consume EXPRESSION
14214                let expr = self.parse_or()?;
14215                col_def.materialized_expr = Some(Box::new(expr));
14216            } else if matches!(
14217                self.config.dialect,
14218                Some(crate::dialects::DialectType::ClickHouse)
14219            ) && (self.match_identifier("HIERARCHICAL")
14220                || self.match_identifier("IS_OBJECT_ID")
14221                || self.match_identifier("INJECTIVE")
14222                || self.match_identifier("BIDIRECTIONAL"))
14223            {
14224                // ClickHouse dictionary column attributes: HIERARCHICAL, IS_OBJECT_ID, INJECTIVE, BIDIRECTIONAL
14225                // These are flag-like attributes with no value, just skip them
14226            } else if self.match_identifier("TTL") {
14227                // ClickHouse: TTL expr
14228                let expr = self.parse_expression()?;
14229                col_def.ttl_expr = Some(Box::new(expr));
14230            } else if matches!(
14231                self.config.dialect,
14232                Some(crate::dialects::DialectType::ClickHouse)
14233            ) && self.check(TokenType::Settings)
14234                && self.check_next(TokenType::LParen)
14235            {
14236                // ClickHouse: SETTINGS (key = value, ...) on column definition
14237                // Only match parenthesized form; non-parenthesized SETTINGS is statement-level
14238                self.skip(); // consume SETTINGS
14239                self.expect(TokenType::LParen)?;
14240                let mut depth = 1i32;
14241                while !self.is_at_end() && depth > 0 {
14242                    if self.check(TokenType::LParen) {
14243                        depth += 1;
14244                    }
14245                    if self.check(TokenType::RParen) {
14246                        depth -= 1;
14247                        if depth == 0 {
14248                            break;
14249                        }
14250                    }
14251                    self.skip();
14252                }
14253                self.expect(TokenType::RParen)?;
14254            } else {
14255                // Skip unknown column modifiers (DEFERRABLE, CHARACTER SET, etc.)
14256                // to allow parsing to continue
14257                if self.skip_column_modifier() {
14258                    continue;
14259                }
14260                break;
14261            }
14262        }
14263
14264        Ok(col_def)
14265    }
14266
14267    /// Skip optional column modifiers that we don't need to preserve
14268    fn skip_column_modifier(&mut self) -> bool {
14269        // NOT DEFERRABLE, NOT CASESPECIFIC - handle NOT followed by specific keywords
14270        // (NOT NULL is handled earlier in the constraint loop)
14271        if self.check(TokenType::Not) {
14272            // Check what follows NOT
14273            if self.check_next_identifier("DEFERRABLE")
14274                || self.check_next_identifier("CASESPECIFIC")
14275            {
14276                self.skip(); // consume NOT
14277                self.skip(); // consume DEFERRABLE/CASESPECIFIC
14278                return true;
14279            }
14280        }
14281        // DEFERRABLE / NOT DEFERRABLE / INITIALLY DEFERRED / INITIALLY IMMEDIATE
14282        if self.match_identifier("DEFERRABLE")
14283            || self.match_identifier("DEFERRED")
14284            || self.match_identifier("IMMEDIATE")
14285        {
14286            return true;
14287        }
14288        // CHARACTER SET name
14289        if self.match_identifier("CHARACTER") {
14290            self.match_token(TokenType::Set);
14291            // Consume charset name (can be multiple parts like LATIN, utf8_bin, etc.)
14292            let _ = self.match_token(TokenType::Var) || self.match_token(TokenType::Identifier);
14293            return true;
14294        }
14295        // UPPERCASE, CASESPECIFIC
14296        if self.match_identifier("UPPERCASE") || self.match_identifier("CASESPECIFIC") {
14297            return true;
14298        }
14299        // Note: COMPRESS, FORMAT, TITLE, and INLINE LENGTH are now properly parsed and stored in ColumnDef
14300        false
14301    }
14302
14303    /// Parse Teradata-specific table options after CREATE TABLE AS
14304    /// Returns (with_data, with_statistics, teradata_indexes)
14305    fn parse_teradata_table_options(&mut self) -> (Option<bool>, Option<bool>, Vec<TeradataIndex>) {
14306        let mut with_data = None;
14307        let mut with_statistics = None;
14308        let mut teradata_indexes = Vec::new();
14309
14310        loop {
14311            // WITH DATA [AND STATISTICS] / WITH NO DATA [AND NO STATISTICS]
14312            if self.match_token(TokenType::With) {
14313                let no = self.match_token(TokenType::No); // optional NO
14314                self.match_identifier("DATA");
14315                with_data = Some(!no); // WITH DATA = true, WITH NO DATA = false
14316                                       // Optional AND [NO] STATISTICS
14317                if self.match_token(TokenType::And) {
14318                    let no_stats = self.match_token(TokenType::No); // optional NO
14319                    self.match_identifier("STATISTICS");
14320                    with_statistics = Some(!no_stats); // AND STATISTICS = true, AND NO STATISTICS = false
14321                }
14322                continue;
14323            }
14324            // NO PRIMARY INDEX
14325            if self.match_token(TokenType::No) {
14326                self.match_token(TokenType::PrimaryKey);
14327                self.match_token(TokenType::Index);
14328                teradata_indexes.push(TeradataIndex {
14329                    kind: TeradataIndexKind::NoPrimary,
14330                    name: None,
14331                    columns: Vec::new(),
14332                });
14333                // Consume optional comma separator between index specs
14334                self.match_token(TokenType::Comma);
14335                continue;
14336            }
14337            // PRIMARY AMP INDEX / PRIMARY INDEX
14338            if self.match_token(TokenType::PrimaryKey) {
14339                let is_amp = self.match_identifier("AMP");
14340                self.match_token(TokenType::Index);
14341                // Optional index name
14342                let name = if self.is_identifier_token() && !self.check(TokenType::LParen) {
14343                    Some(self.advance().text)
14344                } else {
14345                    None
14346                };
14347                // Optional column list
14348                let columns = if self.match_token(TokenType::LParen) {
14349                    let cols = self.parse_identifier_list_raw();
14350                    self.match_token(TokenType::RParen);
14351                    cols
14352                } else {
14353                    Vec::new()
14354                };
14355                teradata_indexes.push(TeradataIndex {
14356                    kind: if is_amp {
14357                        TeradataIndexKind::PrimaryAmp
14358                    } else {
14359                        TeradataIndexKind::Primary
14360                    },
14361                    name,
14362                    columns,
14363                });
14364                // Consume optional comma separator between index specs
14365                self.match_token(TokenType::Comma);
14366                continue;
14367            }
14368            // UNIQUE [PRIMARY] INDEX
14369            if self.match_token(TokenType::Unique) {
14370                let is_primary = self.match_token(TokenType::PrimaryKey);
14371                self.match_token(TokenType::Index);
14372                // Optional index name
14373                let name = if self.is_identifier_token() {
14374                    Some(self.advance().text)
14375                } else {
14376                    None
14377                };
14378                // Optional column list
14379                let columns = if self.match_token(TokenType::LParen) {
14380                    let cols = self.parse_identifier_list_raw();
14381                    self.match_token(TokenType::RParen);
14382                    cols
14383                } else {
14384                    Vec::new()
14385                };
14386                teradata_indexes.push(TeradataIndex {
14387                    kind: if is_primary {
14388                        TeradataIndexKind::UniquePrimary
14389                    } else {
14390                        TeradataIndexKind::Unique
14391                    },
14392                    name,
14393                    columns,
14394                });
14395                // Consume optional comma separator between index specs
14396                self.match_token(TokenType::Comma);
14397                continue;
14398            }
14399            // Plain INDEX (non-primary, non-unique)
14400            if self.match_token(TokenType::Index) {
14401                // Optional index name
14402                let name = if self.is_identifier_token() && !self.check(TokenType::LParen) {
14403                    Some(self.advance().text)
14404                } else {
14405                    None
14406                };
14407                // Optional column list
14408                let columns = if self.match_token(TokenType::LParen) {
14409                    let cols = self.parse_identifier_list_raw();
14410                    self.match_token(TokenType::RParen);
14411                    cols
14412                } else {
14413                    Vec::new()
14414                };
14415                teradata_indexes.push(TeradataIndex {
14416                    kind: TeradataIndexKind::Secondary,
14417                    name,
14418                    columns,
14419                });
14420                // Consume optional comma separator between index specs
14421                self.match_token(TokenType::Comma);
14422                continue;
14423            }
14424            break;
14425        }
14426
14427        (with_data, with_statistics, teradata_indexes)
14428    }
14429
14430    /// Parse Teradata table options after name before column list (comma-separated)
14431    fn parse_teradata_post_name_options(&mut self) -> Vec<String> {
14432        // Options begin with a comma after the table name.
14433        if !self.match_token(TokenType::Comma) {
14434            return Vec::new();
14435        }
14436
14437        let mut options = Vec::new();
14438        let mut current_tokens: Vec<(String, TokenType)> = Vec::new();
14439        let mut paren_depth = 0;
14440        let mut in_value = false;
14441
14442        while !self.is_at_end() {
14443            if self.check(TokenType::LParen) && paren_depth == 0 {
14444                if !in_value {
14445                    // Column list begins
14446                    break;
14447                }
14448                let mut is_terminal = false;
14449                if let Some((last_text, last_type)) = current_tokens.last() {
14450                    let last_upper = last_text.to_ascii_uppercase();
14451                    is_terminal = matches!(last_type, TokenType::Number | TokenType::String)
14452                        || matches!(
14453                            last_upper.as_str(),
14454                            "ON" | "OFF"
14455                                | "DEFAULT"
14456                                | "NEVER"
14457                                | "ALWAYS"
14458                                | "MINIMUM"
14459                                | "MAXIMUM"
14460                                | "BYTES"
14461                                | "KBYTES"
14462                                | "KILOBYTES"
14463                                | "PERCENT"
14464                        );
14465                }
14466                if is_terminal {
14467                    break;
14468                }
14469            }
14470
14471            let token = self.advance();
14472
14473            match token.token_type {
14474                TokenType::LParen => {
14475                    paren_depth += 1;
14476                }
14477                TokenType::RParen => {
14478                    if paren_depth > 0 {
14479                        paren_depth -= 1;
14480                        if paren_depth == 0 && in_value {
14481                            in_value = false;
14482                        }
14483                    }
14484                }
14485                TokenType::Eq => {
14486                    if paren_depth == 0 {
14487                        in_value = true;
14488                    }
14489                }
14490                TokenType::Comma => {
14491                    if paren_depth == 0 {
14492                        let option = self.join_teradata_option_tokens(current_tokens);
14493                        if !option.is_empty() {
14494                            options.push(option);
14495                        }
14496                        current_tokens = Vec::new();
14497                        in_value = false;
14498                        continue;
14499                    }
14500                }
14501                _ => {}
14502            }
14503
14504            let text = if token.token_type == TokenType::QuotedIdentifier {
14505                let quote_char = if self.config.dialect == Some(crate::dialects::DialectType::MySQL)
14506                    || self.config.dialect == Some(crate::dialects::DialectType::SingleStore)
14507                    || self.config.dialect == Some(crate::dialects::DialectType::Doris)
14508                    || self.config.dialect == Some(crate::dialects::DialectType::StarRocks)
14509                {
14510                    '`'
14511                } else {
14512                    '"'
14513                };
14514                format!("{}{}{}", quote_char, token.text, quote_char)
14515            } else if token.token_type == TokenType::String {
14516                format!("'{}'", token.text)
14517            } else {
14518                token.text.clone()
14519            };
14520
14521            let mut join_type = token.token_type;
14522            if join_type == TokenType::Percent && token.text.eq_ignore_ascii_case("PERCENT") {
14523                // Treat PERCENT as an identifier to preserve spacing (e.g., "1 PERCENT")
14524                join_type = TokenType::Identifier;
14525            }
14526            current_tokens.push((text, join_type));
14527        }
14528
14529        if !current_tokens.is_empty() {
14530            let option = self.join_teradata_option_tokens(current_tokens);
14531            if !option.is_empty() {
14532                options.push(option);
14533            }
14534        }
14535
14536        options
14537    }
14538
14539    /// Parse identifier list for Teradata indexes, returning raw strings
14540    fn parse_identifier_list_raw(&mut self) -> Vec<String> {
14541        let mut identifiers = Vec::new();
14542        loop {
14543            if self.is_identifier_token() || self.is_identifier_or_keyword_token() {
14544                identifiers.push(self.advance().text);
14545            }
14546            if !self.match_token(TokenType::Comma) {
14547                break;
14548            }
14549        }
14550        identifiers
14551    }
14552
14553    /// Parse GENERATED column constraint after GENERATED token has been consumed.
14554    /// Handles three forms:
14555    /// 1. GENERATED [BY DEFAULT | ALWAYS] AS IDENTITY [...] -> GeneratedAsIdentity
14556    /// 2. GENERATED ALWAYS AS (expr) [STORED|VIRTUAL] -> ComputedColumn
14557    /// 3. GENERATED ALWAYS AS ROW START|END [HIDDEN] -> GeneratedAsRow
14558    fn parse_generated_column_constraint(&mut self, col_def: &mut ColumnDef) -> Result<()> {
14559        let always;
14560        let mut on_null = false;
14561
14562        // BY DEFAULT [ON NULL] | ALWAYS
14563        if self.match_token(TokenType::By) {
14564            self.expect(TokenType::Default)?;
14565            on_null = self.match_keywords(&[TokenType::On, TokenType::Null]);
14566            always = false;
14567        } else {
14568            self.expect(TokenType::Always)?;
14569            always = true;
14570        }
14571
14572        // Expect AS
14573        self.expect(TokenType::As)?;
14574
14575        // Check what follows AS
14576        if self.check(TokenType::Row) {
14577            // GENERATED ALWAYS AS ROW START|END [HIDDEN]
14578            self.skip(); // consume ROW
14579            let start = if self.match_token(TokenType::Start) {
14580                true
14581            } else {
14582                self.expect(TokenType::End)?;
14583                false
14584            };
14585            let hidden = self.match_identifier("HIDDEN");
14586            col_def
14587                .constraints
14588                .push(ColumnConstraint::GeneratedAsRow(GeneratedAsRow {
14589                    start,
14590                    hidden,
14591                }));
14592            col_def
14593                .constraint_order
14594                .push(ConstraintType::GeneratedAsRow);
14595        } else if self.check(TokenType::Identity) {
14596            // GENERATED [BY DEFAULT | ALWAYS] AS IDENTITY [(...)]
14597            self.skip(); // consume IDENTITY
14598
14599            let mut start = None;
14600            let mut increment = None;
14601            let mut minvalue = None;
14602            let mut maxvalue = None;
14603            let mut cycle = None;
14604
14605            // Optional sequence options in parentheses
14606            if self.match_token(TokenType::LParen) {
14607                loop {
14608                    if self.match_token(TokenType::Start) {
14609                        self.match_token(TokenType::With);
14610                        start = Some(Box::new(self.parse_unary()?));
14611                    } else if self.match_token(TokenType::Increment) {
14612                        self.match_token(TokenType::By);
14613                        increment = Some(Box::new(self.parse_unary()?));
14614                    } else if self.match_token(TokenType::Minvalue) {
14615                        minvalue = Some(Box::new(self.parse_unary()?));
14616                    } else if self.match_token(TokenType::Maxvalue) {
14617                        maxvalue = Some(Box::new(self.parse_unary()?));
14618                    } else if self.match_token(TokenType::Cycle) {
14619                        cycle = Some(true);
14620                    } else if self.match_keywords(&[TokenType::No, TokenType::Cycle]) {
14621                        cycle = Some(false);
14622                    } else if self.check(TokenType::RParen) {
14623                        break;
14624                    } else {
14625                        self.skip();
14626                    }
14627                }
14628                self.expect(TokenType::RParen)?;
14629            }
14630
14631            col_def
14632                .constraints
14633                .push(ColumnConstraint::GeneratedAsIdentity(GeneratedAsIdentity {
14634                    always,
14635                    on_null,
14636                    start,
14637                    increment,
14638                    minvalue,
14639                    maxvalue,
14640                    cycle,
14641                }));
14642            col_def
14643                .constraint_order
14644                .push(ConstraintType::GeneratedAsIdentity);
14645        } else if self.check(TokenType::LParen) {
14646            // GENERATED ALWAYS AS (expr) [STORED|VIRTUAL]
14647            self.skip(); // consume LParen
14648            let expr = self.parse_expression()?;
14649            self.expect(TokenType::RParen)?;
14650
14651            // Check for STORED or VIRTUAL
14652            let (persisted, persistence_kind) = if self.match_identifier("STORED") {
14653                (true, Some("STORED".to_string()))
14654            } else if self.match_identifier("VIRTUAL") {
14655                (false, Some("VIRTUAL".to_string()))
14656            } else {
14657                (false, None)
14658            };
14659
14660            col_def
14661                .constraints
14662                .push(ColumnConstraint::ComputedColumn(ComputedColumn {
14663                    expression: Box::new(expr),
14664                    persisted,
14665                    not_null: false,
14666                    persistence_kind,
14667                    data_type: None,
14668                }));
14669            col_def
14670                .constraint_order
14671                .push(ConstraintType::ComputedColumn);
14672        } else {
14673            // Fallback: treat as GENERATED AS IDENTITY without explicit IDENTITY keyword
14674            col_def
14675                .constraints
14676                .push(ColumnConstraint::GeneratedAsIdentity(GeneratedAsIdentity {
14677                    always,
14678                    on_null,
14679                    start: None,
14680                    increment: None,
14681                    minvalue: None,
14682                    maxvalue: None,
14683                    cycle: None,
14684                }));
14685            col_def
14686                .constraint_order
14687                .push(ConstraintType::GeneratedAsIdentity);
14688        }
14689        Ok(())
14690    }
14691
14692    /// Parse AS (expr) [STORED|VIRTUAL|PERSISTED] [TYPE] [NOT NULL] for computed columns.
14693    /// Called after AS token has been consumed and we've confirmed LParen follows.
14694    /// SingleStore: AS (expr) PERSISTED TYPE NOT NULL
14695    fn parse_as_computed_column(&mut self, col_def: &mut ColumnDef) -> Result<()> {
14696        self.expect(TokenType::LParen)?;
14697        let expr = self.parse_expression()?;
14698        self.expect(TokenType::RParen)?;
14699
14700        // Check for STORED, VIRTUAL, or PERSISTED
14701        let (persisted, persistence_kind) = if self.match_identifier("STORED") {
14702            (true, Some("STORED".to_string()))
14703        } else if self.match_identifier("VIRTUAL") {
14704            (false, Some("VIRTUAL".to_string()))
14705        } else if self.match_identifier("PERSISTED") {
14706            (true, Some("PERSISTED".to_string()))
14707        } else {
14708            (false, None)
14709        };
14710
14711        // For PERSISTED columns, check for optional data type (SingleStore: PERSISTED TYPE NOT NULL)
14712        // Also check for AUTO keyword for SingleStore: PERSISTED AUTO NOT NULL
14713        let data_type = if persistence_kind.as_deref() == Some("PERSISTED") {
14714            // Check if next token looks like a data type (not NOT, not end of input, not comma/rparen)
14715            if !self.is_at_end()
14716                && !self.check(TokenType::Not)
14717                && !self.check(TokenType::Comma)
14718                && !self.check(TokenType::RParen)
14719                && !self.check(TokenType::Semicolon)
14720            {
14721                let tok = self.peek();
14722                // Check for AUTO keyword (SingleStore: PERSISTED AUTO)
14723                if tok.text.eq_ignore_ascii_case("AUTO") {
14724                    self.skip(); // consume AUTO
14725                    None // AUTO is not a data type, just a modifier
14726                } else if tok.token_type.is_keyword()
14727                    || tok.token_type == TokenType::Identifier
14728                    || tok.token_type == TokenType::Var
14729                {
14730                    Some(self.parse_data_type()?)
14731                } else {
14732                    None
14733                }
14734            } else {
14735                None
14736            }
14737        } else {
14738            None
14739        };
14740
14741        // For PERSISTED columns, check for NOT NULL
14742        let not_null = if persistence_kind.as_deref() == Some("PERSISTED") {
14743            self.match_keywords(&[TokenType::Not, TokenType::Null])
14744        } else {
14745            false
14746        };
14747
14748        col_def
14749            .constraints
14750            .push(ColumnConstraint::ComputedColumn(ComputedColumn {
14751                expression: Box::new(expr),
14752                persisted,
14753                not_null,
14754                persistence_kind,
14755                data_type,
14756            }));
14757        col_def
14758            .constraint_order
14759            .push(ConstraintType::ComputedColumn);
14760        Ok(())
14761    }
14762
14763    /// Parse PERIOD FOR SYSTEM_TIME (start_col, end_col) as a table constraint.
14764    /// Returns None if this is not actually PERIOD FOR SYSTEM_TIME (e.g., just a column named PERIOD).
14765    fn parse_period_for_system_time_table_constraint(&mut self) -> Result<Option<TableConstraint>> {
14766        // Save position for possible retreat
14767        let saved = self.current;
14768
14769        if self.match_identifier("PERIOD") {
14770            // Check if followed by FOR SYSTEM_TIME
14771            if self.match_token(TokenType::For) {
14772                if self.match_identifier("SYSTEM_TIME") {
14773                    // Parse (start_col, end_col)
14774                    self.expect(TokenType::LParen)?;
14775                    let start_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
14776                    self.expect(TokenType::Comma)?;
14777                    let end_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
14778                    self.expect(TokenType::RParen)?;
14779                    return Ok(Some(TableConstraint::PeriodForSystemTime {
14780                        start_col: start_name,
14781                        end_col: end_name,
14782                    }));
14783                }
14784            }
14785        }
14786
14787        // Not PERIOD FOR SYSTEM_TIME, retreat
14788        self.current = saved;
14789        Ok(None)
14790    }
14791
14792    /// Parse MySQL table options that appear after the closing paren of column definitions.
14793    /// Handles ENGINE=val, AUTO_INCREMENT=val, DEFAULT CHARSET=val, ROW_FORMAT=val,
14794    /// COMMENT='val', COLLATE=val, etc.
14795    fn parse_mysql_table_options(&mut self) -> Vec<(String, String)> {
14796        let mut options = Vec::new();
14797        loop {
14798            // Skip optional commas between options
14799            self.match_token(TokenType::Comma);
14800
14801            // DEFAULT CHARSET=val or DEFAULT CHARACTER SET=val
14802            if self.check(TokenType::Default) {
14803                let saved = self.current;
14804                self.skip(); // consume DEFAULT
14805                if self.check_identifier("CHARSET") || self.check_identifier("CHARACTER") {
14806                    let is_character = self.check_identifier("CHARACTER");
14807                    let key_part = self.advance().text.to_ascii_uppercase();
14808                    if is_character {
14809                        // CHARACTER SET
14810                        self.match_token(TokenType::Set);
14811                    }
14812                    if self.match_token(TokenType::Eq) {
14813                        let value = if self.check(TokenType::String) {
14814                            let v = format!("'{}'", self.peek().text);
14815                            self.skip();
14816                            v
14817                        } else if self.is_identifier_token()
14818                            || self.is_safe_keyword_as_identifier()
14819                            || self.check(TokenType::Number)
14820                        {
14821                            self.advance().text
14822                        } else {
14823                            self.current = saved;
14824                            break;
14825                        };
14826                        // Normalize CHARSET -> CHARACTER SET
14827                        let key = if is_character || key_part == "CHARSET" {
14828                            "DEFAULT CHARACTER SET".to_string()
14829                        } else {
14830                            format!("DEFAULT {}", key_part)
14831                        };
14832                        options.push((key, value));
14833                        continue;
14834                    }
14835                }
14836                self.current = saved;
14837                break;
14838            }
14839
14840            // ENGINE=val, AUTO_INCREMENT=val, ROW_FORMAT=val, COLLATE=val, KEY_BLOCK_SIZE=val
14841            let is_known_option = self.check_identifier("ENGINE")
14842                || self.check(TokenType::AutoIncrement)
14843                || self.check_identifier("ROW_FORMAT")
14844                || self.check(TokenType::Collate)
14845                || self.check_identifier("KEY_BLOCK_SIZE")
14846                || self.check_identifier("PACK_KEYS")
14847                || self.check_identifier("STATS_AUTO_RECALC")
14848                || self.check_identifier("STATS_PERSISTENT")
14849                || self.check_identifier("STATS_SAMPLE_PAGES")
14850                || self.check_identifier("MAX_ROWS")
14851                || self.check_identifier("MIN_ROWS")
14852                || self.check_identifier("CHECKSUM")
14853                || self.check_identifier("DELAY_KEY_WRITE")
14854                || self.check_identifier("COMPRESSION")
14855                || self.check_identifier("CONNECTION")
14856                || self.check_identifier("TABLESPACE")
14857                || self.check_identifier("ENCRYPTION");
14858
14859            if is_known_option {
14860                let key = self.advance().text.to_ascii_uppercase();
14861                if self.match_token(TokenType::Eq) {
14862                    let value = if self.check(TokenType::String) {
14863                        let v = format!("'{}'", self.peek().text);
14864                        self.skip();
14865                        v
14866                    } else if self.check(TokenType::Number) {
14867                        self.advance().text
14868                    } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
14869                        self.advance().text
14870                    } else {
14871                        break;
14872                    };
14873                    options.push((key, value));
14874                    continue;
14875                }
14876                break;
14877            }
14878
14879            // COMMENT='val' (Comment is a keyword token type)
14880            if self.check(TokenType::Comment) {
14881                let saved = self.current;
14882                self.skip(); // consume COMMENT
14883                if self.match_token(TokenType::Eq) {
14884                    if self.check(TokenType::String) {
14885                        let v = format!("'{}'", self.peek().text);
14886                        self.skip();
14887                        options.push(("COMMENT".to_string(), v));
14888                        continue;
14889                    }
14890                } else if self.check(TokenType::String) {
14891                    let v = format!("'{}'", self.peek().text);
14892                    self.skip();
14893                    options.push(("COMMENT".to_string(), v));
14894                    continue;
14895                }
14896                self.current = saved;
14897                break;
14898            }
14899
14900            // CHARACTER SET=val or CHARSET=val (without DEFAULT prefix)
14901            if self.check_identifier("CHARACTER") || self.check_identifier("CHARSET") {
14902                let saved = self.current;
14903                let is_character = self.check_identifier("CHARACTER");
14904                self.skip(); // consume CHARACTER or CHARSET
14905                if is_character {
14906                    // CHARACTER SET
14907                    if !self.match_token(TokenType::Set) {
14908                        self.current = saved;
14909                        break;
14910                    }
14911                }
14912                if self.match_token(TokenType::Eq) {
14913                    let value = if self.check(TokenType::String) {
14914                        let v = format!("'{}'", self.peek().text);
14915                        self.skip();
14916                        v
14917                    } else if self.is_identifier_token()
14918                        || self.is_safe_keyword_as_identifier()
14919                        || self.check(TokenType::Number)
14920                    {
14921                        self.advance().text
14922                    } else {
14923                        self.current = saved;
14924                        break;
14925                    };
14926                    options.push(("CHARACTER SET".to_string(), value));
14927                    continue;
14928                }
14929                self.current = saved;
14930                break;
14931            }
14932
14933            break;
14934        }
14935        options
14936    }
14937
14938    /// Parse Hive-specific table properties that appear after column definitions.
14939    /// Handles: ROW FORMAT (SERDE/DELIMITED), STORED AS/BY, LOCATION, TBLPROPERTIES
14940    fn parse_hive_table_properties(&mut self) -> Result<Vec<Expression>> {
14941        let mut properties = Vec::new();
14942
14943        loop {
14944            // ROW FORMAT SERDE 'class' [WITH SERDEPROPERTIES (...)]
14945            // ROW FORMAT DELIMITED [FIELDS TERMINATED BY ...] [...]
14946            if self.match_token(TokenType::Row) {
14947                if let Some(row_format) = self.parse_row()? {
14948                    properties.push(row_format);
14949                    continue;
14950                }
14951            }
14952
14953            // STORED AS INPUTFORMAT 'input' OUTPUTFORMAT 'output'
14954            // STORED AS format_name
14955            // STORED BY 'storage_handler_class'
14956            if self.match_identifier("STORED") {
14957                if self.match_token(TokenType::By) {
14958                    // STORED BY 'storage_handler_class'
14959                    let handler = self.parse_string()?.unwrap_or(Expression::Null(Null));
14960                    properties.push(Expression::StorageHandlerProperty(Box::new(
14961                        StorageHandlerProperty {
14962                            this: Box::new(handler),
14963                        },
14964                    )));
14965                    continue;
14966                } else if self.match_token(TokenType::As) {
14967                    // STORED AS INPUTFORMAT 'x' OUTPUTFORMAT 'y' or STORED AS format
14968                    if self.match_token(TokenType::InputFormat) {
14969                        let input_format = self.parse_string()?;
14970                        let output_format = if self.match_identifier("OUTPUTFORMAT") {
14971                            self.parse_string()?
14972                        } else {
14973                            None
14974                        };
14975                        // Use InputOutputFormat inside FileFormatProperty.this
14976                        let io_format =
14977                            Expression::InputOutputFormat(Box::new(InputOutputFormat {
14978                                input_format: input_format.map(Box::new),
14979                                output_format: output_format.map(Box::new),
14980                            }));
14981                        properties.push(Expression::FileFormatProperty(Box::new(
14982                            FileFormatProperty {
14983                                this: Some(Box::new(io_format)),
14984                                expressions: vec![],
14985                                hive_format: Some(Box::new(Expression::Boolean(BooleanLiteral {
14986                                    value: true,
14987                                }))),
14988                            },
14989                        )));
14990                        continue;
14991                    } else {
14992                        // STORED AS format_name (e.g., STORED AS TEXTFILE, STORED AS ORC)
14993                        let format = if self.check(TokenType::String) {
14994                            Expression::Literal(Box::new(Literal::String(
14995                                self.advance().text.clone(),
14996                            )))
14997                        } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier()
14998                        {
14999                            Expression::Identifier(Identifier::new(self.advance().text.clone()))
15000                        } else {
15001                            break;
15002                        };
15003                        properties.push(Expression::FileFormatProperty(Box::new(
15004                            FileFormatProperty {
15005                                this: Some(Box::new(format)),
15006                                expressions: vec![],
15007                                hive_format: Some(Box::new(Expression::Boolean(BooleanLiteral {
15008                                    value: true,
15009                                }))),
15010                            },
15011                        )));
15012                        continue;
15013                    }
15014                }
15015            }
15016
15017            // USING format_name (Databricks/Spark) e.g., USING DELTA, USING PARQUET
15018            // This is similar to STORED AS but uses different syntax
15019            if self.match_token(TokenType::Using) {
15020                // Parse the format name (e.g., DELTA, PARQUET, ICEBERG, etc.)
15021                let format = if self.check(TokenType::String) {
15022                    Expression::Literal(Box::new(Literal::String(self.advance().text.clone())))
15023                } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
15024                    Expression::Identifier(Identifier::new(self.advance().text.clone()))
15025                } else {
15026                    break;
15027                };
15028                // Create FileFormatProperty WITHOUT hive_format to signal USING syntax
15029                properties.push(Expression::FileFormatProperty(Box::new(
15030                    FileFormatProperty {
15031                        this: Some(Box::new(format)),
15032                        expressions: vec![],
15033                        hive_format: None, // None indicates USING syntax (not STORED AS)
15034                    },
15035                )));
15036                continue;
15037            }
15038
15039            // LOCATION 'path'
15040            if self.match_identifier("LOCATION") {
15041                let path = self.parse_string()?.unwrap_or(Expression::Null(Null));
15042                properties.push(Expression::LocationProperty(Box::new(LocationProperty {
15043                    this: Box::new(path),
15044                })));
15045                continue;
15046            }
15047
15048            // TBLPROPERTIES ('key'='value', ...)
15049            if self.match_identifier("TBLPROPERTIES") {
15050                // Parse the property list manually since parse_property doesn't handle key=value
15051                self.expect(TokenType::LParen)?;
15052                let mut prop_exprs = Vec::new();
15053                loop {
15054                    if self.check(TokenType::RParen) {
15055                        break;
15056                    }
15057                    // Parse 'key'='value' or key=value
15058                    let key = self.parse_primary()?;
15059                    if self.match_token(TokenType::Eq) {
15060                        let value = self.parse_primary()?;
15061                        prop_exprs.push(Expression::Eq(Box::new(BinaryOp::new(key, value))));
15062                    } else {
15063                        prop_exprs.push(key);
15064                    }
15065                    if !self.match_token(TokenType::Comma) {
15066                        break;
15067                    }
15068                }
15069                self.expect(TokenType::RParen)?;
15070                properties.push(Expression::Properties(Box::new(Properties {
15071                    expressions: prop_exprs,
15072                })));
15073                continue;
15074            }
15075
15076            // DISTRIBUTED BY HASH (col1, col2) [BUCKETS n] (StarRocks/Doris)
15077            if self.match_identifier("DISTRIBUTED") {
15078                if let Some(dist_prop) = self.parse_distributed_property()? {
15079                    properties.push(dist_prop);
15080                    continue;
15081                }
15082            }
15083
15084            // CLUSTERED BY (col, col, ...) [SORTED BY (col, col, ...)] INTO n BUCKETS (Hive/Athena)
15085            if self.match_identifier("CLUSTERED") {
15086                self.expect(TokenType::By)?;
15087                self.expect(TokenType::LParen)?;
15088                let expressions = self.parse_expression_list()?;
15089                self.expect(TokenType::RParen)?;
15090
15091                // Optional SORTED BY (col, col, ...)
15092                let sorted_by = if self.match_identifier("SORTED") {
15093                    self.expect(TokenType::By)?;
15094                    self.expect(TokenType::LParen)?;
15095                    let sorted_exprs = self.parse_expression_list()?;
15096                    self.expect(TokenType::RParen)?;
15097                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
15098                        expressions: sorted_exprs,
15099                    }))))
15100                } else {
15101                    None
15102                };
15103
15104                // INTO n BUCKETS
15105                let buckets = if self.match_token(TokenType::Into) {
15106                    let num = self.parse_expression()?;
15107                    if !self.match_identifier("BUCKETS") {
15108                        return Err(self.parse_error("Expected BUCKETS after INTO <n>"));
15109                    }
15110                    Some(Box::new(num))
15111                } else {
15112                    None
15113                };
15114
15115                properties.push(Expression::ClusteredByProperty(Box::new(
15116                    ClusteredByProperty {
15117                        expressions,
15118                        sorted_by,
15119                        buckets,
15120                    },
15121                )));
15122                continue;
15123            }
15124
15125            // PARTITIONED BY (col, col, ...) or PARTITIONED BY (col, BUCKET(n, col), ...) (Hive/Athena/Iceberg)
15126            if self.match_identifier("PARTITIONED") {
15127                self.expect(TokenType::By)?;
15128                self.expect(TokenType::LParen)?;
15129
15130                let mut partition_exprs = Vec::new();
15131                loop {
15132                    if self.check(TokenType::RParen) {
15133                        break;
15134                    }
15135
15136                    // Check for transform functions like BUCKET(n, col), TRUNCATE(n, col), etc.
15137                    if self.check_identifier("BUCKET") || self.check_identifier("TRUNCATE") {
15138                        let func_name = self.advance().text.clone();
15139                        self.expect(TokenType::LParen)?;
15140                        let args = self.parse_expression_list()?;
15141                        self.expect(TokenType::RParen)?;
15142
15143                        // Create a Function expression for BUCKET/TRUNCATE
15144                        partition_exprs.push(Expression::Function(Box::new(Function {
15145                            name: func_name,
15146                            args,
15147                            distinct: false,
15148                            trailing_comments: Vec::new(),
15149                            use_bracket_syntax: false,
15150                            no_parens: false,
15151                            quoted: false,
15152                            span: None,
15153                            inferred_type: None,
15154                        })));
15155                    } else {
15156                        // Try to parse as column definition (name data_type) for Hive-style partitioned by
15157                        // e.g., PARTITIONED BY (y INT, z STRING)
15158                        let saved_pos = self.current;
15159                        let mut parsed_as_column = false;
15160                        // Allow type keywords (like DATE, TIMESTAMP) as column names in PARTITIONED BY
15161                        if self.check(TokenType::Var)
15162                            || self.check(TokenType::Identifier)
15163                            || self.check(TokenType::Date)
15164                            || self.check(TokenType::Timestamp)
15165                            || self.check(TokenType::Int)
15166                            || self.check(TokenType::BigInt)
15167                            || self.check(TokenType::SmallInt)
15168                            || self.check(TokenType::TinyInt)
15169                            || self.check(TokenType::Float)
15170                            || self.check(TokenType::Double)
15171                            || self.check(TokenType::Boolean)
15172                        {
15173                            let col_name = self.advance().text.clone();
15174                            // Check if next token looks like a data type
15175                            if self.check(TokenType::Var)
15176                                || self.check(TokenType::Identifier)
15177                                || self.check(TokenType::Int)
15178                                || self.check(TokenType::BigInt)
15179                                || self.check(TokenType::SmallInt)
15180                                || self.check(TokenType::TinyInt)
15181                                || self.check(TokenType::Float)
15182                                || self.check(TokenType::Double)
15183                                || self.check(TokenType::Boolean)
15184                                || self.check(TokenType::Date)
15185                                || self.check(TokenType::Timestamp)
15186                            {
15187                                let type_text = self.peek().text.to_ascii_uppercase();
15188                                let is_type = matches!(
15189                                    type_text.as_str(),
15190                                    "INT"
15191                                        | "INTEGER"
15192                                        | "BIGINT"
15193                                        | "SMALLINT"
15194                                        | "TINYINT"
15195                                        | "FLOAT"
15196                                        | "DOUBLE"
15197                                        | "DECIMAL"
15198                                        | "NUMERIC"
15199                                        | "STRING"
15200                                        | "VARCHAR"
15201                                        | "CHAR"
15202                                        | "BINARY"
15203                                        | "BOOLEAN"
15204                                        | "DATE"
15205                                        | "TIMESTAMP"
15206                                        | "DATETIME"
15207                                        | "ARRAY"
15208                                        | "MAP"
15209                                        | "STRUCT"
15210                                );
15211                                if is_type {
15212                                    // Parse as column definition
15213                                    let data_type = self.parse_data_type()?;
15214                                    // Store as ColumnDef expression
15215                                    partition_exprs.push(Expression::ColumnDef(Box::new(
15216                                        crate::expressions::ColumnDef::new(col_name, data_type),
15217                                    )));
15218                                    parsed_as_column = true;
15219                                }
15220                            }
15221                        }
15222                        if !parsed_as_column {
15223                            // Backtrack and parse as regular expression
15224                            self.current = saved_pos;
15225                            partition_exprs.push(self.parse_expression()?);
15226                        }
15227                    }
15228
15229                    if !self.match_token(TokenType::Comma) {
15230                        break;
15231                    }
15232                }
15233                self.expect(TokenType::RParen)?;
15234
15235                properties.push(Expression::PartitionedByProperty(Box::new(
15236                    PartitionedByProperty {
15237                        this: Box::new(Expression::Tuple(Box::new(Tuple {
15238                            expressions: partition_exprs,
15239                        }))),
15240                    },
15241                )));
15242                continue;
15243            }
15244
15245            // No more Hive properties
15246            break;
15247        }
15248
15249        Ok(properties)
15250    }
15251
15252    /// Parse table-level properties that appear after the closing paren of column definitions.
15253    /// Currently handles TSQL WITH(SYSTEM_VERSIONING=ON(...)).
15254    fn parse_post_table_properties(&mut self) -> Result<Vec<Expression>> {
15255        let mut properties = Vec::new();
15256
15257        // Doris/StarRocks: UNIQUE KEY (cols) or DUPLICATE KEY (cols) after column definitions
15258        // These are table key properties that define the distribution/sort key
15259        let is_doris_starrocks = matches!(
15260            self.config.dialect,
15261            Some(crate::dialects::DialectType::Doris)
15262                | Some(crate::dialects::DialectType::StarRocks)
15263        );
15264        if is_doris_starrocks {
15265            // UNIQUE KEY (c1, c2, ...) - defines unique key columns
15266            if self.match_text_seq(&["UNIQUE", "KEY"]) {
15267                let exprs = self.parse_composite_key_expressions()?;
15268                properties.push(Expression::UniqueKeyProperty(Box::new(
15269                    crate::expressions::UniqueKeyProperty { expressions: exprs },
15270                )));
15271            }
15272            // DUPLICATE KEY (c1, c2, ...) - defines duplicate key columns
15273            else if self.match_text_seq(&["DUPLICATE", "KEY"]) {
15274                let exprs = self.parse_composite_key_expressions()?;
15275                properties.push(Expression::DuplicateKeyProperty(Box::new(
15276                    crate::expressions::DuplicateKeyProperty { expressions: exprs },
15277                )));
15278            }
15279
15280            // DISTRIBUTED BY HASH (col1, col2) [BUCKETS n] - comes after UNIQUE KEY / DUPLICATE KEY
15281            if self.match_identifier("DISTRIBUTED") {
15282                if let Some(dist_prop) = self.parse_distributed_property()? {
15283                    properties.push(dist_prop);
15284                }
15285            }
15286
15287            // PROPERTIES ('key'='value', ...) - comes after DISTRIBUTED BY
15288            if self.match_identifier("PROPERTIES") {
15289                let props = self.parse_options_list()?;
15290                if !props.is_empty() {
15291                    properties.push(Expression::Properties(Box::new(Properties {
15292                        expressions: props,
15293                    })));
15294                }
15295            }
15296        }
15297
15298        // Check for WITH( that might contain SYSTEM_VERSIONING
15299        // We need to be careful not to consume a WITH that is meant for WITH properties
15300        // or other purposes. We only handle WITH(SYSTEM_VERSIONING=...) here.
15301        if self.check(TokenType::With) {
15302            // Look ahead: WITH followed by ( followed by SYSTEM_VERSIONING
15303            let saved = self.current;
15304            if self.match_token(TokenType::With) {
15305                if self.match_token(TokenType::LParen) {
15306                    if self.check_identifier("SYSTEM_VERSIONING") {
15307                        self.skip(); // consume SYSTEM_VERSIONING
15308                        self.expect(TokenType::Eq)?;
15309
15310                        let on = if self.match_token(TokenType::On) {
15311                            true
15312                        } else if self.match_identifier("OFF") {
15313                            false
15314                        } else {
15315                            return Err(
15316                                self.parse_error("Expected ON or OFF after SYSTEM_VERSIONING=")
15317                            );
15318                        };
15319
15320                        let mut history_table = None;
15321                        let mut data_consistency = None;
15322
15323                        // Optional parameters: ON(HISTORY_TABLE=..., DATA_CONSISTENCY_CHECK=...)
15324                        if on && self.match_token(TokenType::LParen) {
15325                            loop {
15326                                if self.check(TokenType::RParen) {
15327                                    break;
15328                                }
15329                                if self.match_identifier("HISTORY_TABLE") {
15330                                    self.expect(TokenType::Eq)?;
15331                                    // Parse table reference (could be [dbo].[table])
15332                                    let table_ref = self.parse_table_ref()?;
15333                                    history_table = Some(Expression::Table(Box::new(table_ref)));
15334                                } else if self.match_identifier("DATA_CONSISTENCY_CHECK") {
15335                                    self.expect(TokenType::Eq)?;
15336                                    let val = self.expect_identifier_or_keyword()?;
15337                                    data_consistency = Some(Expression::Identifier(
15338                                        crate::expressions::Identifier::new(val),
15339                                    ));
15340                                } else if self.check(TokenType::RParen) {
15341                                    break;
15342                                } else {
15343                                    self.skip();
15344                                }
15345                                self.match_token(TokenType::Comma);
15346                            }
15347                            self.expect(TokenType::RParen)?;
15348                        }
15349
15350                        self.expect(TokenType::RParen)?; // close WITH(...)
15351
15352                        properties.push(Expression::WithSystemVersioningProperty(Box::new(
15353                            WithSystemVersioningProperty {
15354                                on: if on {
15355                                    Some(Box::new(Expression::Boolean(
15356                                        crate::expressions::BooleanLiteral { value: true },
15357                                    )))
15358                                } else {
15359                                    None
15360                                },
15361                                this: history_table.map(Box::new),
15362                                data_consistency: data_consistency.map(Box::new),
15363                                retention_period: None,
15364                                with_: Some(Box::new(Expression::Boolean(
15365                                    crate::expressions::BooleanLiteral { value: true },
15366                                ))),
15367                            },
15368                        )));
15369                    } else {
15370                        // Not SYSTEM_VERSIONING, retreat
15371                        self.current = saved;
15372                    }
15373                } else {
15374                    // Not WITH(...), retreat
15375                    self.current = saved;
15376                }
15377            }
15378        }
15379
15380        Ok(properties)
15381    }
15382
15383    /// Parse composite key expressions for UNIQUE KEY (cols) or DUPLICATE KEY (cols)
15384    /// Returns a vector of column identifiers
15385    fn parse_composite_key_expressions(&mut self) -> Result<Vec<Expression>> {
15386        self.expect(TokenType::LParen)?;
15387        let mut expressions = Vec::new();
15388        loop {
15389            if let Some(id) = self.parse_id_var()? {
15390                expressions.push(id);
15391            } else {
15392                break;
15393            }
15394            if !self.match_token(TokenType::Comma) {
15395                break;
15396            }
15397        }
15398        self.expect(TokenType::RParen)?;
15399        Ok(expressions)
15400    }
15401
15402    /// Parse a table-level constraint
15403    fn parse_table_constraint(&mut self) -> Result<TableConstraint> {
15404        // Optional constraint name
15405        let name = if self.match_token(TokenType::Constraint) {
15406            // Use safe keyword version to accept keywords as constraint names (e.g., CONSTRAINT identity CHECK ...)
15407            Some(self.expect_identifier_or_safe_keyword_with_quoted()?)
15408        } else {
15409            None
15410        };
15411
15412        self.parse_constraint_definition(name)
15413    }
15414
15415    /// Parse constraint definition (after optional CONSTRAINT name)
15416    fn parse_constraint_definition(&mut self, name: Option<Identifier>) -> Result<TableConstraint> {
15417        if self.match_keywords(&[TokenType::PrimaryKey, TokenType::Key]) {
15418            // PRIMARY KEY [CLUSTERED|NONCLUSTERED] [name] (col1, col2) [INCLUDE (col3, col4)]
15419            // MySQL allows: PRIMARY KEY pk_name (col1, col2)
15420            // TSQL allows: PRIMARY KEY CLUSTERED (col1, col2)
15421
15422            // Check for TSQL CLUSTERED/NONCLUSTERED modifier
15423            let clustered = if self.check_identifier("CLUSTERED") {
15424                self.skip();
15425                Some("CLUSTERED".to_string())
15426            } else if self.check_identifier("NONCLUSTERED") {
15427                self.skip();
15428                Some("NONCLUSTERED".to_string())
15429            } else {
15430                None
15431            };
15432
15433            let actual_name = if name.is_none() && !self.check(TokenType::LParen) {
15434                if matches!(
15435                    self.config.dialect,
15436                    Some(crate::dialects::DialectType::ClickHouse)
15437                ) {
15438                    // ClickHouse: PRIMARY KEY col (without parentheses)
15439                    None
15440                } else if self.is_identifier_token() || self.check(TokenType::QuotedIdentifier) {
15441                    Some(self.expect_identifier_with_quoted()?)
15442                } else if self.check(TokenType::String)
15443                    && matches!(
15444                        self.config.dialect,
15445                        Some(crate::dialects::DialectType::MySQL)
15446                    )
15447                {
15448                    // MySQL: double-quoted strings can be used as constraint names
15449                    // e.g., PRIMARY KEY "pk_name" (id) -> PRIMARY KEY `pk_name` (id)
15450                    let s = self.advance().text.clone();
15451                    Some(Identifier {
15452                        name: s,
15453                        quoted: true,
15454                        trailing_comments: Vec::new(),
15455                        span: None,
15456                    })
15457                } else {
15458                    None
15459                }
15460            } else {
15461                name.clone()
15462            };
15463            // ClickHouse: PRIMARY KEY col without parens — parse single column
15464            let columns = if matches!(
15465                self.config.dialect,
15466                Some(crate::dialects::DialectType::ClickHouse)
15467            ) && !self.check(TokenType::LParen)
15468                && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
15469            {
15470                let col_name = self.expect_identifier_or_keyword_with_quoted()?;
15471                vec![col_name]
15472            } else {
15473                self.expect(TokenType::LParen)?;
15474                // ClickHouse: allow empty PRIMARY KEY ()
15475                let cols = if self.check(TokenType::RParen) {
15476                    Vec::new()
15477                } else if matches!(
15478                    self.config.dialect,
15479                    Some(crate::dialects::DialectType::ClickHouse)
15480                ) {
15481                    // ClickHouse: PRIMARY KEY(v1, gcd(v1, v2)) - expressions allowed
15482                    let mut exprs = Vec::new();
15483                    loop {
15484                        let expr = self.parse_expression()?;
15485                        let name = self.expression_to_sql(&expr);
15486                        exprs.push(Identifier::new(name));
15487                        if !self.match_token(TokenType::Comma) {
15488                            break;
15489                        }
15490                    }
15491                    exprs
15492                } else {
15493                    self.parse_index_identifier_list()?
15494                };
15495                self.expect(TokenType::RParen)?;
15496                cols
15497            };
15498            // Parse optional INCLUDE (columns)
15499            let include_columns = if self.match_identifier("INCLUDE") {
15500                self.expect(TokenType::LParen)?;
15501                let cols = self.parse_identifier_list()?;
15502                self.expect(TokenType::RParen)?;
15503                cols
15504            } else {
15505                Vec::new()
15506            };
15507            // Parse optional constraint modifiers (ENFORCED, DEFERRABLE, etc.)
15508            let mut modifiers = self.parse_constraint_modifiers();
15509            modifiers.clustered = clustered;
15510            let has_constraint_keyword = name.is_some();
15511            Ok(TableConstraint::PrimaryKey {
15512                name: actual_name.or(name),
15513                columns,
15514                include_columns,
15515                modifiers,
15516                has_constraint_keyword,
15517            })
15518        } else if self.match_token(TokenType::Unique) {
15519            // UNIQUE [CLUSTERED|NONCLUSTERED] [KEY|INDEX] [NULLS NOT DISTINCT] [name] (col1, col2) or UNIQUE column_name
15520            // MySQL allows: UNIQUE KEY name (cols), UNIQUE INDEX name (cols), UNIQUE (cols)
15521            // TSQL allows: UNIQUE CLUSTERED (cols)
15522            // PostgreSQL 15+: UNIQUE NULLS NOT DISTINCT (cols)
15523
15524            // Check for TSQL CLUSTERED/NONCLUSTERED modifier
15525            let clustered = if self.check_identifier("CLUSTERED") {
15526                self.skip();
15527                Some("CLUSTERED".to_string())
15528            } else if self.check_identifier("NONCLUSTERED") {
15529                self.skip();
15530                Some("NONCLUSTERED".to_string())
15531            } else {
15532                None
15533            };
15534
15535            let use_key_keyword =
15536                self.match_token(TokenType::Key) || self.match_token(TokenType::Index);
15537
15538            // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
15539            let nulls_not_distinct = self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]);
15540
15541            // Check for optional constraint name (before columns)
15542            let actual_name = if name.is_none()
15543                && self.is_identifier_token()
15544                && !self.check_next(TokenType::Comma)
15545            {
15546                // Name might be here: UNIQUE KEY idx_name (cols)
15547                if self.check_next(TokenType::LParen) {
15548                    Some(self.expect_identifier_with_quoted()?)
15549                } else {
15550                    None
15551                }
15552            } else {
15553                name.clone()
15554            };
15555
15556            if self.match_token(TokenType::LParen) {
15557                let columns = self.parse_index_identifier_list()?;
15558                self.expect(TokenType::RParen)?;
15559                let mut modifiers = self.parse_constraint_modifiers();
15560                modifiers.clustered = clustered;
15561                if use_key_keyword {
15562                    // UNIQUE KEY/INDEX - use Index constraint type with UNIQUE kind
15563                    Ok(TableConstraint::Index {
15564                        name: actual_name.or(name),
15565                        columns,
15566                        kind: Some("UNIQUE".to_string()),
15567                        modifiers,
15568                        use_key_keyword,
15569                        expression: None,
15570                        index_type: None,
15571                        granularity: None,
15572                    })
15573                } else {
15574                    let has_constraint_keyword = name.is_some();
15575                    Ok(TableConstraint::Unique {
15576                        name: actual_name.or(name),
15577                        columns,
15578                        columns_parenthesized: true,
15579                        modifiers,
15580                        has_constraint_keyword,
15581                        nulls_not_distinct,
15582                    })
15583                }
15584            } else {
15585                // Single column unique (for ALTER TABLE ADD CONSTRAINT name UNIQUE colname)
15586                let col_name = self.expect_identifier()?;
15587                let mut modifiers = self.parse_constraint_modifiers();
15588                modifiers.clustered = clustered;
15589                let has_constraint_keyword = name.is_some();
15590                Ok(TableConstraint::Unique {
15591                    name: actual_name.or(name),
15592                    columns: vec![Identifier::new(col_name)],
15593                    columns_parenthesized: false,
15594                    modifiers,
15595                    has_constraint_keyword,
15596                    nulls_not_distinct,
15597                })
15598            }
15599        } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
15600            // FOREIGN KEY (col1) [REFERENCES other_table(col2)] [ON DELETE ...] [ON UPDATE ...]
15601            self.expect(TokenType::LParen)?;
15602            let columns = self.parse_identifier_list()?;
15603            self.expect(TokenType::RParen)?;
15604            if self.match_token(TokenType::References) {
15605                let references = self.parse_foreign_key_ref()?;
15606                let modifiers = self.parse_constraint_modifiers();
15607                Ok(TableConstraint::ForeignKey {
15608                    name,
15609                    columns,
15610                    references: Some(references),
15611                    on_delete: None,
15612                    on_update: None,
15613                    modifiers,
15614                })
15615            } else {
15616                // No REFERENCES - parse optional ON DELETE/ON UPDATE directly
15617                let mut on_delete = None;
15618                let mut on_update = None;
15619                loop {
15620                    if self.check(TokenType::On) {
15621                        let saved = self.current;
15622                        self.skip(); // consume ON
15623                        if self.match_token(TokenType::Delete) {
15624                            on_delete = Some(self.parse_referential_action()?);
15625                        } else if self.match_token(TokenType::Update) {
15626                            on_update = Some(self.parse_referential_action()?);
15627                        } else {
15628                            self.current = saved;
15629                            break;
15630                        }
15631                    } else {
15632                        break;
15633                    }
15634                }
15635                let modifiers = self.parse_constraint_modifiers();
15636                Ok(TableConstraint::ForeignKey {
15637                    name,
15638                    columns,
15639                    references: None,
15640                    on_delete,
15641                    on_update,
15642                    modifiers,
15643                })
15644            }
15645        } else if self.match_token(TokenType::Check) {
15646            // CHECK (expression) or CHECK (SELECT ...) or ClickHouse: CHECK expression (without parens)
15647            let expression = if self.match_token(TokenType::LParen) {
15648                let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
15649                    // SELECT/WITH in CHECK constraint — parse directly, no Subquery wrapper
15650                    // The generator already wraps CHECK content in parens
15651                    self.parse_statement()?
15652                } else {
15653                    self.parse_expression()?
15654                };
15655                self.expect(TokenType::RParen)?;
15656                expr
15657            } else if matches!(
15658                self.config.dialect,
15659                Some(crate::dialects::DialectType::ClickHouse)
15660            ) {
15661                self.parse_or()?
15662            } else {
15663                self.expect(TokenType::LParen)?;
15664                unreachable!()
15665            };
15666            let modifiers = self.parse_constraint_modifiers();
15667            Ok(TableConstraint::Check {
15668                name,
15669                expression,
15670                modifiers,
15671            })
15672        } else if self.match_token(TokenType::Exclude) {
15673            // PostgreSQL EXCLUDE constraint
15674            // EXCLUDE [USING method] (element WITH operator, ...) [INCLUDE (cols)] [WHERE (expr)] [WITH (params)]
15675            let using = if self.match_token(TokenType::Using) {
15676                Some(self.expect_identifier()?)
15677            } else {
15678                None
15679            };
15680
15681            self.expect(TokenType::LParen)?;
15682            let mut elements = Vec::new();
15683            loop {
15684                // Parse element expression: may be a function call like INT4RANGE(vid, nid)
15685                // or column name possibly with operator class, ASC/DESC, NULLS FIRST/LAST
15686                let mut expr_parts = Vec::new();
15687                let mut paren_depth = 0;
15688                while !self.is_at_end() {
15689                    if self.check(TokenType::LParen) {
15690                        paren_depth += 1;
15691                        expr_parts.push(self.advance().text);
15692                    } else if self.check(TokenType::RParen) {
15693                        if paren_depth == 0 {
15694                            break;
15695                        }
15696                        paren_depth -= 1;
15697                        expr_parts.push(self.advance().text);
15698                    } else if paren_depth == 0 && self.check(TokenType::With) {
15699                        break;
15700                    } else if self.check(TokenType::String) {
15701                        // Preserve string literal quotes
15702                        let token = self.advance();
15703                        expr_parts.push(format!("'{}'", token.text));
15704                    } else {
15705                        expr_parts.push(self.advance().text);
15706                    }
15707                }
15708                let expression = expr_parts
15709                    .join(" ")
15710                    .replace(" (", "(")
15711                    .replace(" )", ")")
15712                    .replace("( ", "(")
15713                    .replace(" ,", ",");
15714
15715                // Parse WITH operator
15716                self.expect(TokenType::With)?;
15717                let operator = self.advance().text.clone();
15718
15719                elements.push(ExcludeElement {
15720                    expression,
15721                    operator,
15722                });
15723
15724                if !self.match_token(TokenType::Comma) {
15725                    break;
15726                }
15727            }
15728            self.expect(TokenType::RParen)?;
15729
15730            // Parse optional INCLUDE (columns)
15731            let include_columns = if self.match_identifier("INCLUDE") {
15732                self.expect(TokenType::LParen)?;
15733                let cols = self.parse_identifier_list()?;
15734                self.expect(TokenType::RParen)?;
15735                cols
15736            } else {
15737                Vec::new()
15738            };
15739
15740            // Parse optional WITH (storage_parameters)
15741            let with_params = if self.match_token(TokenType::With) {
15742                self.expect(TokenType::LParen)?;
15743                let mut params = Vec::new();
15744                loop {
15745                    let key = self.expect_identifier()?;
15746                    self.expect(TokenType::Eq)?;
15747                    let val = self.advance().text.clone();
15748                    params.push((key, val));
15749                    if !self.match_token(TokenType::Comma) {
15750                        break;
15751                    }
15752                }
15753                self.expect(TokenType::RParen)?;
15754                params
15755            } else {
15756                Vec::new()
15757            };
15758
15759            // Parse optional USING INDEX TABLESPACE tablespace_name
15760            let using_index_tablespace =
15761                if self.check(TokenType::Using) && self.check_next(TokenType::Index) {
15762                    self.skip(); // consume USING
15763                    self.skip(); // consume INDEX
15764                    if self.match_identifier("TABLESPACE") {
15765                        Some(self.expect_identifier()?)
15766                    } else {
15767                        None
15768                    }
15769                } else {
15770                    None
15771                };
15772
15773            // Parse optional WHERE clause
15774            let where_clause = if self.match_token(TokenType::Where) {
15775                self.expect(TokenType::LParen)?;
15776                let expr = self.parse_expression()?;
15777                self.expect(TokenType::RParen)?;
15778                Some(Box::new(expr))
15779            } else {
15780                None
15781            };
15782
15783            let modifiers = self.parse_constraint_modifiers();
15784            Ok(TableConstraint::Exclude {
15785                name,
15786                using,
15787                elements,
15788                include_columns,
15789                where_clause,
15790                with_params,
15791                using_index_tablespace,
15792                modifiers,
15793            })
15794        } else if matches!(
15795            self.config.dialect,
15796            Some(crate::dialects::DialectType::ClickHouse)
15797        ) && self.check_identifier("ASSUME")
15798        {
15799            // ClickHouse: CONSTRAINT name ASSUME expression
15800            // Used for query optimization assumptions
15801            self.skip(); // consume ASSUME
15802            let expression = if self.match_token(TokenType::LParen) {
15803                // ASSUME (expr) or ASSUME (SELECT ...)
15804                let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
15805                    self.parse_statement()?
15806                } else {
15807                    self.parse_expression()?
15808                };
15809                self.expect(TokenType::RParen)?;
15810                expr
15811            } else {
15812                self.parse_expression()?
15813            };
15814            Ok(TableConstraint::Assume { name, expression })
15815        } else {
15816            Err(self.parse_error("Expected PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK, or EXCLUDE"))
15817        }
15818    }
15819
15820    /// Parse INDEX/KEY table constraint for MySQL
15821    /// Syntax: [FULLTEXT|SPATIAL] {INDEX|KEY} [name] [USING {BTREE|HASH}] (columns)
15822    ///     or: [FULLTEXT|SPATIAL] {INDEX|KEY} [USING {BTREE|HASH}] (columns)  -- no name
15823    fn parse_index_table_constraint(&mut self) -> Result<TableConstraint> {
15824        // Check for FULLTEXT or SPATIAL prefix
15825        let kind = if self.match_identifier("FULLTEXT") {
15826            Some("FULLTEXT".to_string())
15827        } else if self.match_identifier("SPATIAL") {
15828            Some("SPATIAL".to_string())
15829        } else {
15830            None
15831        };
15832
15833        // Consume INDEX or KEY keyword, track which was used
15834        let use_key_keyword = if self.match_token(TokenType::Key) {
15835            true
15836        } else {
15837            self.match_token(TokenType::Index);
15838            false
15839        };
15840
15841        // Check for USING before index name (MySQL allows: INDEX USING BTREE (col))
15842        let early_using = if self.check(TokenType::Using) {
15843            self.match_token(TokenType::Using);
15844            if self.match_identifier("BTREE") {
15845                Some("BTREE".to_string())
15846            } else if self.match_identifier("HASH") {
15847                Some("HASH".to_string())
15848            } else {
15849                None
15850            }
15851        } else {
15852            None
15853        };
15854
15855        // Optional index name (only if next token is not LParen or Using)
15856        let name = if !self.check(TokenType::LParen)
15857            && !self.check(TokenType::Using)
15858            && self.is_identifier_token()
15859        {
15860            Some(Identifier::new(self.advance().text))
15861        } else {
15862            None
15863        };
15864
15865        // Check for USING after index name (if not already parsed)
15866        let late_using = if early_using.is_none() && self.match_token(TokenType::Using) {
15867            if self.match_identifier("BTREE") {
15868                Some("BTREE".to_string())
15869            } else if self.match_identifier("HASH") {
15870                Some("HASH".to_string())
15871            } else {
15872                None
15873            }
15874        } else {
15875            None
15876        };
15877
15878        // Parse columns (with optional prefix length and DESC)
15879        self.expect(TokenType::LParen)?;
15880        let columns = self.parse_index_identifier_list()?;
15881        self.expect(TokenType::RParen)?;
15882
15883        // Parse optional constraint modifiers (USING after columns, COMMENT, etc.)
15884        let mut modifiers = self.parse_constraint_modifiers();
15885
15886        // Set the using value from wherever we found it
15887        // Both early_using (before name) and late_using (after name, before columns) mean USING is before columns
15888        if early_using.is_some() {
15889            modifiers.using = early_using;
15890            modifiers.using_before_columns = true;
15891        } else if late_using.is_some() {
15892            modifiers.using = late_using;
15893            modifiers.using_before_columns = true; // USING was after name but before columns
15894        }
15895        // If using was found in parse_constraint_modifiers (after columns), using_before_columns stays false
15896
15897        Ok(TableConstraint::Index {
15898            name,
15899            columns,
15900            kind,
15901            modifiers,
15902            use_key_keyword,
15903            expression: None,
15904            index_type: None,
15905            granularity: None,
15906        })
15907    }
15908
15909    /// Parse constraint modifiers like ENFORCED, DEFERRABLE, NORELY, USING, etc.
15910    fn parse_constraint_modifiers(&mut self) -> ConstraintModifiers {
15911        let mut modifiers = ConstraintModifiers::default();
15912        loop {
15913            if self.match_token(TokenType::Not) {
15914                // NOT ENFORCED, NOT DEFERRABLE, NOT VALID
15915                if self.match_identifier("ENFORCED") {
15916                    modifiers.enforced = Some(false);
15917                } else if self.match_identifier("DEFERRABLE") {
15918                    modifiers.deferrable = Some(false);
15919                } else if self.match_identifier("VALID") {
15920                    modifiers.not_valid = true;
15921                }
15922            } else if self.match_identifier("ENFORCED") {
15923                modifiers.enforced = Some(true);
15924            } else if self.match_identifier("DEFERRABLE") {
15925                modifiers.deferrable = Some(true);
15926            } else if self.match_identifier("INITIALLY") {
15927                // INITIALLY DEFERRED or INITIALLY IMMEDIATE
15928                if self.match_identifier("DEFERRED") {
15929                    modifiers.initially_deferred = Some(true);
15930                } else if self.match_identifier("IMMEDIATE") {
15931                    modifiers.initially_deferred = Some(false);
15932                }
15933            } else if self.match_identifier("NORELY") {
15934                modifiers.norely = true;
15935            } else if self.match_identifier("RELY") {
15936                modifiers.rely = true;
15937            } else if self.match_token(TokenType::Using) {
15938                // USING BTREE or USING HASH (MySQL)
15939                if self.match_identifier("BTREE") {
15940                    modifiers.using = Some("BTREE".to_string());
15941                } else if self.match_identifier("HASH") {
15942                    modifiers.using = Some("HASH".to_string());
15943                }
15944            } else if self.match_token(TokenType::Comment) {
15945                // MySQL index COMMENT 'text'
15946                if self.check(TokenType::String) {
15947                    modifiers.comment = Some(self.advance().text);
15948                }
15949            } else if self.match_identifier("VISIBLE") {
15950                modifiers.visible = Some(true);
15951            } else if self.match_identifier("INVISIBLE") {
15952                modifiers.visible = Some(false);
15953            } else if self.match_identifier("ENGINE_ATTRIBUTE") {
15954                // MySQL ENGINE_ATTRIBUTE = 'value'
15955                self.match_token(TokenType::Eq);
15956                if self.check(TokenType::String) {
15957                    modifiers.engine_attribute = Some(self.advance().text);
15958                }
15959            } else if self.check(TokenType::With) {
15960                let saved_with = self.current;
15961                self.skip(); // consume WITH
15962                if self.match_identifier("PARSER") {
15963                    // MySQL WITH PARSER name
15964                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
15965                        modifiers.with_parser = Some(self.advance().text);
15966                    }
15967                } else if self.check(TokenType::LParen) {
15968                    // TSQL: WITH (PAD_INDEX=ON, STATISTICS_NORECOMPUTE=OFF, ...)
15969                    // Parse and store the options
15970                    self.skip(); // consume (
15971                    loop {
15972                        if self.check(TokenType::RParen) || self.is_at_end() {
15973                            break;
15974                        }
15975                        // Parse KEY=VALUE pair
15976                        let key = self.advance().text.clone();
15977                        if self.match_token(TokenType::Eq) {
15978                            let value = self.advance().text.clone();
15979                            modifiers.with_options.push((key, value));
15980                        }
15981                        if !self.match_token(TokenType::Comma) {
15982                            break;
15983                        }
15984                    }
15985                    let _ = self.match_token(TokenType::RParen);
15986                } else {
15987                    // Not WITH PARSER or WITH (...), backtrack
15988                    self.current = saved_with;
15989                    break;
15990                }
15991            } else if self.check(TokenType::On) {
15992                let saved_on = self.current;
15993                self.skip(); // consume ON
15994                if self.match_identifier("CONFLICT") {
15995                    // SQLite ON CONFLICT action: ROLLBACK, ABORT, FAIL, IGNORE, REPLACE
15996                    if self.match_token(TokenType::Rollback) {
15997                        modifiers.on_conflict = Some("ROLLBACK".to_string());
15998                    } else if self.match_identifier("ABORT") {
15999                        modifiers.on_conflict = Some("ABORT".to_string());
16000                    } else if self.match_identifier("FAIL") {
16001                        modifiers.on_conflict = Some("FAIL".to_string());
16002                    } else if self.match_token(TokenType::Ignore) {
16003                        modifiers.on_conflict = Some("IGNORE".to_string());
16004                    } else if self.match_token(TokenType::Replace) {
16005                        modifiers.on_conflict = Some("REPLACE".to_string());
16006                    }
16007                } else if self.is_identifier_token() || self.check(TokenType::QuotedIdentifier) {
16008                    // TSQL: ON [filegroup] - parse and store
16009                    let quoted = self.check(TokenType::QuotedIdentifier);
16010                    let name = self.advance().text.clone();
16011                    modifiers.on_filegroup = Some(Identifier {
16012                        name,
16013                        quoted,
16014                        trailing_comments: Vec::new(),
16015                        span: None,
16016                    });
16017                } else {
16018                    // Unknown ON clause, backtrack
16019                    self.current = saved_on;
16020                    break;
16021                }
16022            } else {
16023                break;
16024            }
16025        }
16026        modifiers
16027    }
16028
16029    /// Parse foreign key reference
16030    fn parse_foreign_key_ref(&mut self) -> Result<ForeignKeyRef> {
16031        let table = self.parse_table_ref()?;
16032
16033        let columns = if self.match_token(TokenType::LParen) {
16034            let cols = self.parse_identifier_list()?;
16035            self.expect(TokenType::RParen)?;
16036            cols
16037        } else {
16038            Vec::new()
16039        };
16040
16041        // Handle optional MATCH clause (MATCH FULL, MATCH PARTIAL, MATCH SIMPLE)
16042        // MATCH clause comes BEFORE ON DELETE/ON UPDATE in PostgreSQL
16043        let match_type = if self.match_token(TokenType::Match) {
16044            if self.check(TokenType::Full) {
16045                self.skip();
16046                Some(MatchType::Full)
16047            } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
16048                let text = self.advance().text.to_ascii_uppercase();
16049                match text.as_str() {
16050                    "PARTIAL" => Some(MatchType::Partial),
16051                    "SIMPLE" => Some(MatchType::Simple),
16052                    _ => None,
16053                }
16054            } else {
16055                None
16056            }
16057        } else {
16058            None
16059        };
16060
16061        // ON DELETE and ON UPDATE can appear in either order
16062        let mut on_delete = None;
16063        let mut on_update = None;
16064        let mut on_update_first = false;
16065        let mut first_clause = true;
16066
16067        // Try parsing up to 2 ON clauses
16068        for _ in 0..2 {
16069            if on_delete.is_none() && self.match_keywords(&[TokenType::On, TokenType::Delete]) {
16070                on_delete = Some(self.parse_referential_action()?);
16071            } else if on_update.is_none()
16072                && self.match_keywords(&[TokenType::On, TokenType::Update])
16073            {
16074                if first_clause {
16075                    on_update_first = true;
16076                }
16077                on_update = Some(self.parse_referential_action()?);
16078            } else {
16079                break;
16080            }
16081            first_clause = false;
16082        }
16083
16084        // MATCH clause can also appear after ON DELETE/ON UPDATE
16085        let mut match_after_actions = false;
16086        let match_type = if match_type.is_none() && self.match_token(TokenType::Match) {
16087            match_after_actions = on_delete.is_some() || on_update.is_some();
16088            if self.check(TokenType::Full) {
16089                self.skip();
16090                Some(MatchType::Full)
16091            } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
16092                let text = self.advance().text.to_ascii_uppercase();
16093                match text.as_str() {
16094                    "PARTIAL" => Some(MatchType::Partial),
16095                    "SIMPLE" => Some(MatchType::Simple),
16096                    _ => None,
16097                }
16098            } else {
16099                None
16100            }
16101        } else {
16102            match_type
16103        };
16104
16105        // Handle optional DEFERRABLE / NOT DEFERRABLE
16106        let deferrable = if self.match_identifier("DEFERRABLE") {
16107            Some(true)
16108        } else if self.match_token(TokenType::Not) && self.match_identifier("DEFERRABLE") {
16109            Some(false)
16110        } else {
16111            None
16112        };
16113
16114        Ok(ForeignKeyRef {
16115            table,
16116            columns,
16117            on_delete,
16118            on_update,
16119            on_update_first,
16120            match_type,
16121            match_after_actions,
16122            constraint_name: None, // Will be set by caller if CONSTRAINT was used
16123            deferrable,
16124            has_foreign_key_keywords: false, // Will be set by caller if FOREIGN KEY preceded REFERENCES
16125        })
16126    }
16127
16128    /// Parse referential action (CASCADE, SET NULL, etc.)
16129    fn parse_referential_action(&mut self) -> Result<ReferentialAction> {
16130        if self.match_token(TokenType::Cascade) {
16131            Ok(ReferentialAction::Cascade)
16132        } else if self.match_keywords(&[TokenType::Set, TokenType::Null]) {
16133            Ok(ReferentialAction::SetNull)
16134        } else if self.match_keywords(&[TokenType::Set, TokenType::Default]) {
16135            Ok(ReferentialAction::SetDefault)
16136        } else if self.match_token(TokenType::Restrict) {
16137            Ok(ReferentialAction::Restrict)
16138        } else if self.match_token(TokenType::No) {
16139            // NO ACTION - NO is a token, ACTION is an identifier
16140            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ACTION") {
16141                self.skip();
16142            }
16143            Ok(ReferentialAction::NoAction)
16144        } else {
16145            Err(self.parse_error("Expected CASCADE, SET NULL, SET DEFAULT, RESTRICT, or NO ACTION"))
16146        }
16147    }
16148
16149    /// Parse Snowflake TAG clause: TAG (key='value', key2='value2')
16150    fn parse_tags(&mut self) -> Result<Tags> {
16151        self.expect(TokenType::LParen)?;
16152        let mut expressions = Vec::new();
16153
16154        loop {
16155            // Parse key = 'value' as a Property expression
16156            let key = self.expect_identifier_or_keyword()?;
16157            self.expect(TokenType::Eq)?;
16158            let value = self.parse_primary()?;
16159
16160            // Create a Property expression: key = value
16161            expressions.push(Expression::Property(Box::new(Property {
16162                this: Box::new(Expression::Identifier(Identifier::new(key))),
16163                value: Some(Box::new(value)),
16164            })));
16165
16166            if !self.match_token(TokenType::Comma) {
16167                break;
16168            }
16169        }
16170
16171        self.expect(TokenType::RParen)?;
16172
16173        Ok(Tags { expressions })
16174    }
16175
16176    /// Parse CREATE VIEW
16177    fn parse_create_view(
16178        &mut self,
16179        or_replace: bool,
16180        or_alter: bool,
16181        materialized: bool,
16182        temporary: bool,
16183        algorithm: Option<String>,
16184        definer: Option<String>,
16185        security: Option<FunctionSecurity>,
16186        secure: bool,
16187    ) -> Result<Expression> {
16188        self.expect(TokenType::View)?;
16189
16190        // Handle IF NOT EXISTS
16191        let if_not_exists =
16192            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
16193
16194        let name = self.parse_table_ref()?;
16195
16196        // ClickHouse: UUID 'xxx' clause after view name
16197        if matches!(
16198            self.config.dialect,
16199            Some(crate::dialects::DialectType::ClickHouse)
16200        ) && self.check_identifier("UUID")
16201        {
16202            self.skip(); // consume UUID
16203            let _ = self.advance(); // consume UUID string value
16204        }
16205
16206        // ClickHouse: ON CLUSTER clause (after view name)
16207        let on_cluster = self.parse_on_cluster_clause()?;
16208
16209        // ClickHouse: TO destination_table clause
16210        let to_table = if self.match_token(TokenType::To) {
16211            Some(self.parse_table_ref()?)
16212        } else {
16213            None
16214        };
16215
16216        // Snowflake: COPY GRANTS (before column list)
16217        let copy_grants = self.match_text_seq(&["COPY", "GRANTS"]);
16218
16219        // For materialized views, column definitions can include data types: (c1 INT, c2 INT)
16220        // This applies to Doris, ClickHouse, and potentially other dialects
16221        // We need to parse this as a schema instead of simple column names
16222        // Track if we parsed a schema (with types) vs simple columns
16223        let mut schema: Option<Schema> = None;
16224        let mut unique_key: Option<UniqueKeyProperty> = None;
16225
16226        // Optional column list with optional COMMENT and OPTIONS per column
16227        let columns = if self.check(TokenType::LParen) {
16228            // For materialized views or ClickHouse views, try to parse as schema with typed columns
16229            if materialized
16230                || matches!(
16231                    self.config.dialect,
16232                    Some(crate::dialects::DialectType::ClickHouse)
16233                )
16234            {
16235                // Save position to backtrack if needed
16236                let saved_pos = self.current;
16237
16238                // Try to parse as schema (with typed columns)
16239                if let Some(Expression::Schema(parsed_schema)) = self.parse_schema()? {
16240                    schema = Some(*parsed_schema);
16241
16242                    // Doris: KEY (columns) after schema
16243                    if self.match_text_seq(&["KEY"]) {
16244                        let exprs = self.parse_composite_key_expressions()?;
16245                        unique_key = Some(UniqueKeyProperty { expressions: exprs });
16246                    }
16247
16248                    Vec::new() // Use schema instead of columns
16249                } else {
16250                    // Backtrack and parse as simple columns
16251                    self.current = saved_pos;
16252                    self.parse_view_columns()?
16253                }
16254            } else {
16255                self.parse_view_columns()?
16256            }
16257        } else {
16258            Vec::new()
16259        };
16260
16261        // Snowflake: COPY GRANTS can also appear after column list
16262        let copy_grants = copy_grants || self.match_text_seq(&["COPY", "GRANTS"]);
16263
16264        // Presto/Trino/StarRocks: SECURITY DEFINER/INVOKER/NONE (after view name, before AS)
16265        // MySQL also allows SQL SECURITY DEFINER/INVOKER after the view name
16266        // This differs from MySQL's SQL SECURITY which can also come before VIEW keyword
16267        let (security, security_sql_style, security_after_name) = if security.is_some() {
16268            // MySQL-style SQL SECURITY was parsed before VIEW keyword
16269            (security, true, false)
16270        } else if self.check_identifier("SQL")
16271            && self.current + 1 < self.tokens.len()
16272            && self.tokens[self.current + 1]
16273                .text
16274                .eq_ignore_ascii_case("SECURITY")
16275        {
16276            // SQL SECURITY after view name
16277            self.skip(); // consume SQL
16278            self.skip(); // consume SECURITY
16279            let sec = if self.match_identifier("DEFINER") {
16280                Some(FunctionSecurity::Definer)
16281            } else if self.match_identifier("INVOKER") {
16282                Some(FunctionSecurity::Invoker)
16283            } else if self.match_identifier("NONE") {
16284                Some(FunctionSecurity::None)
16285            } else {
16286                None
16287            };
16288            (sec, true, true)
16289        } else if self.match_identifier("SECURITY") {
16290            // Presto-style SECURITY after view name
16291            let sec = if self.match_identifier("DEFINER") {
16292                Some(FunctionSecurity::Definer)
16293            } else if self.match_identifier("INVOKER") {
16294                Some(FunctionSecurity::Invoker)
16295            } else if self.match_identifier("NONE") {
16296                Some(FunctionSecurity::None)
16297            } else {
16298                None
16299            };
16300            (sec, false, false)
16301        } else {
16302            (None, true, false)
16303        };
16304
16305        // Snowflake: COMMENT = 'text'
16306        let view_comment = if self.match_token(TokenType::Comment) {
16307            // Match = or skip if not present (some dialects use COMMENT='text')
16308            let _ = self.match_token(TokenType::Eq);
16309            Some(self.expect_string()?)
16310        } else {
16311            None
16312        };
16313
16314        // Snowflake: TAG (name='value', ...)
16315        let tags = if self.match_identifier("TAG") {
16316            let mut tag_list = Vec::new();
16317            if self.match_token(TokenType::LParen) {
16318                loop {
16319                    let tag_name = self.expect_identifier()?;
16320                    let tag_value = if self.match_token(TokenType::Eq) {
16321                        self.expect_string()?
16322                    } else {
16323                        String::new()
16324                    };
16325                    tag_list.push((tag_name, tag_value));
16326                    if !self.match_token(TokenType::Comma) {
16327                        break;
16328                    }
16329                }
16330                self.expect(TokenType::RParen)?;
16331            }
16332            tag_list
16333        } else {
16334            Vec::new()
16335        };
16336
16337        // BigQuery: OPTIONS (key=value, ...)
16338        let options = if self.match_identifier("OPTIONS") {
16339            self.parse_options_list()?
16340        } else {
16341            Vec::new()
16342        };
16343
16344        // Doris: BUILD IMMEDIATE/DEFERRED for materialized views
16345        let build = if self.match_identifier("BUILD") {
16346            if self.match_identifier("IMMEDIATE") {
16347                Some("IMMEDIATE".to_string())
16348            } else if self.match_identifier("DEFERRED") {
16349                Some("DEFERRED".to_string())
16350            } else {
16351                // Unexpected token after BUILD - try to consume it
16352                let value = self.expect_identifier_or_keyword()?;
16353                Some(value.to_ascii_uppercase())
16354            }
16355        } else {
16356            None
16357        };
16358
16359        // Doris: REFRESH COMPLETE/AUTO ON MANUAL/COMMIT/SCHEDULE [EVERY n UNIT] [STARTS 'datetime']
16360        // ClickHouse: REFRESH AFTER interval / REFRESH EVERY interval [OFFSET interval] [RANDOMIZE FOR interval] [APPEND]
16361        let refresh = if self.match_token(TokenType::Refresh) {
16362            if matches!(
16363                self.config.dialect,
16364                Some(crate::dialects::DialectType::ClickHouse)
16365            ) {
16366                // ClickHouse REFRESH syntax: consume tokens until AS/POPULATE/TO/ENGINE or end
16367                while !self.is_at_end()
16368                    && !self.check(TokenType::As)
16369                    && !self.check_identifier("POPULATE")
16370                    && !self.check_identifier("TO")
16371                    && !self.check_identifier("APPEND")
16372                    && !self.check_identifier("ENGINE")
16373                    && !self.check(TokenType::Semicolon)
16374                {
16375                    self.skip();
16376                }
16377                // Consume APPEND if present (REFRESH ... APPEND TO target)
16378                let _ = self.match_identifier("APPEND");
16379                None
16380            } else {
16381                Some(Box::new(self.parse_refresh_trigger_property()?))
16382            }
16383        } else {
16384            None
16385        };
16386
16387        // ClickHouse: TO destination_table after REFRESH ... APPEND
16388        // e.g., CREATE MATERIALIZED VIEW v REFRESH AFTER 1 SECOND APPEND TO tab (cols) EMPTY AS ...
16389        let to_table = if to_table.is_none() && self.match_token(TokenType::To) {
16390            Some(self.parse_table_ref()?)
16391        } else {
16392            to_table
16393        };
16394
16395        // ClickHouse: column definitions after REFRESH ... APPEND TO tab (cols)
16396        if schema.is_none()
16397            && self.check(TokenType::LParen)
16398            && matches!(
16399                self.config.dialect,
16400                Some(crate::dialects::DialectType::ClickHouse)
16401            )
16402        {
16403            let saved_pos = self.current;
16404            if let Some(Expression::Schema(parsed_schema)) = self.parse_schema()? {
16405                schema = Some(*parsed_schema);
16406            } else {
16407                self.current = saved_pos;
16408            }
16409        }
16410
16411        // Redshift: AUTO REFRESH YES|NO for materialized views
16412        let auto_refresh = if self.match_text_seq(&["AUTO", "REFRESH"]) {
16413            if self.match_identifier("YES") {
16414                Some(true)
16415            } else if self.match_identifier("NO") {
16416                Some(false)
16417            } else {
16418                None
16419            }
16420        } else {
16421            None
16422        };
16423
16424        // ClickHouse: Parse table properties (ENGINE, ORDER BY, SAMPLE, SETTINGS, TTL, etc.)
16425        // These appear after column definitions but before AS clause for materialized views
16426        let mut table_properties = Vec::new();
16427        if materialized
16428            && matches!(
16429                self.config.dialect,
16430                Some(crate::dialects::DialectType::ClickHouse)
16431            )
16432        {
16433            self.parse_clickhouse_table_properties(&mut table_properties)?;
16434        }
16435
16436        // ClickHouse: POPULATE / EMPTY keywords before AS in materialized views
16437        if materialized
16438            && matches!(
16439                self.config.dialect,
16440                Some(crate::dialects::DialectType::ClickHouse)
16441            )
16442        {
16443            let _ = self.match_identifier("POPULATE");
16444            let _ = self.match_identifier("EMPTY");
16445        }
16446
16447        // AS is optional - some dialects (e.g., Presto) allow SELECT without AS
16448        let has_as = self.match_token(TokenType::As);
16449        if !has_as && !self.check(TokenType::Select) && !self.check(TokenType::With) {
16450            // No AS and no SELECT/WITH means no query - return empty view (for partial statements)
16451            return Ok(Expression::CreateView(Box::new(CreateView {
16452                name,
16453                columns,
16454                query: Expression::Null(Null), // Placeholder for incomplete VIEW
16455                or_replace,
16456                or_alter,
16457                if_not_exists,
16458                materialized,
16459                temporary,
16460                secure,
16461                algorithm,
16462                definer,
16463                security,
16464                security_sql_style,
16465                security_after_name,
16466                query_parenthesized: false,
16467                locking_mode: None,
16468                locking_access: None,
16469                copy_grants,
16470                comment: view_comment,
16471                tags,
16472                options,
16473                build,
16474                refresh,
16475                schema: schema.map(Box::new),
16476                unique_key: unique_key.map(Box::new),
16477                no_schema_binding: false,
16478                auto_refresh,
16479                on_cluster,
16480                to_table,
16481                table_properties,
16482            })));
16483        }
16484
16485        // Parse Teradata LOCKING clause: LOCKING ROW|TABLE|DATABASE FOR ACCESS|READ|WRITE
16486        let mut locking_mode: Option<String> = None;
16487        let mut locking_access: Option<String> = None;
16488        if self.match_token(TokenType::Lock) || self.match_identifier("LOCKING") {
16489            // Capture: ROW, TABLE, DATABASE, etc.
16490            if self.match_token(TokenType::Row) {
16491                locking_mode = Some("ROW".to_string());
16492            } else if self.match_token(TokenType::Table) {
16493                locking_mode = Some("TABLE".to_string());
16494            } else if self.match_token(TokenType::Database) || self.match_identifier("DATABASE") {
16495                locking_mode = Some("DATABASE".to_string());
16496            }
16497            // Capture FOR ACCESS|READ|WRITE
16498            if self.match_token(TokenType::For) {
16499                if self.match_identifier("ACCESS") {
16500                    locking_access = Some("ACCESS".to_string());
16501                } else if self.match_identifier("READ") {
16502                    locking_access = Some("READ".to_string());
16503                } else if self.match_identifier("WRITE") {
16504                    locking_access = Some("WRITE".to_string());
16505                }
16506            }
16507        }
16508
16509        // Use parse_statement to handle SELECT, WITH...SELECT, or (SELECT...)
16510        let query_parenthesized = self.check(TokenType::LParen);
16511        let query = if self.check(TokenType::With) {
16512            self.parse_statement()?
16513        } else if query_parenthesized {
16514            // Handle (SELECT ...) or (WITH ... SELECT ...) - parenthesized query
16515            self.skip(); // consume (
16516            let inner = if self.check(TokenType::With) {
16517                self.parse_statement()?
16518            } else {
16519                self.parse_select()?
16520            };
16521            self.expect(TokenType::RParen)?;
16522            inner
16523        } else {
16524            self.parse_select()?
16525        };
16526
16527        // Redshift: WITH NO SCHEMA BINDING (after the query)
16528        let no_schema_binding = self.match_text_seq(&["WITH", "NO", "SCHEMA", "BINDING"]);
16529
16530        Ok(Expression::CreateView(Box::new(CreateView {
16531            name,
16532            columns,
16533            query,
16534            or_replace,
16535            or_alter,
16536            if_not_exists,
16537            materialized,
16538            temporary,
16539            secure,
16540            algorithm,
16541            definer,
16542            security,
16543            security_sql_style,
16544            security_after_name,
16545            query_parenthesized,
16546            locking_mode,
16547            locking_access,
16548            copy_grants,
16549            comment: view_comment,
16550            tags,
16551            options,
16552            build,
16553            refresh,
16554            schema: schema.map(Box::new),
16555            unique_key: unique_key.map(Box::new),
16556            no_schema_binding,
16557            auto_refresh,
16558            on_cluster,
16559            to_table,
16560            table_properties,
16561        })))
16562    }
16563
16564    /// Parse view column list: (col1, col2 OPTIONS(...) COMMENT 'text', ...)
16565    /// For simple view definitions without data types
16566    fn parse_view_columns(&mut self) -> Result<Vec<ViewColumn>> {
16567        self.expect(TokenType::LParen)?;
16568        let mut cols = Vec::new();
16569        loop {
16570            let col_name = self.expect_identifier()?;
16571            // BigQuery: OPTIONS (key=value, ...) on view column
16572            let options = if self.match_identifier("OPTIONS") {
16573                self.parse_options_list()?
16574            } else {
16575                Vec::new()
16576            };
16577            // Optional COMMENT 'text'
16578            let comment = if self.match_token(TokenType::Comment) {
16579                Some(self.expect_string()?)
16580            } else {
16581                None
16582            };
16583            cols.push(ViewColumn {
16584                name: Identifier::new(col_name),
16585                comment,
16586                options,
16587            });
16588            if !self.match_token(TokenType::Comma) {
16589                break;
16590            }
16591        }
16592        self.expect(TokenType::RParen)?;
16593        Ok(cols)
16594    }
16595
16596    /// Parse CREATE [CLUSTERED|NONCLUSTERED] INDEX
16597    fn parse_create_index_with_clustered(
16598        &mut self,
16599        unique: bool,
16600        clustered: Option<String>,
16601    ) -> Result<Expression> {
16602        self.expect(TokenType::Index)?;
16603
16604        // PostgreSQL: CREATE INDEX CONCURRENTLY idx ON t(c)
16605        let concurrently = self.match_identifier("CONCURRENTLY");
16606
16607        // Handle IF NOT EXISTS
16608        let if_not_exists =
16609            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
16610
16611        // Index name is optional when IF NOT EXISTS is specified (PostgreSQL)
16612        let name = if if_not_exists && self.check(TokenType::On) {
16613            Identifier::new("") // Empty name when omitted
16614        } else {
16615            self.expect_identifier_with_quoted()?
16616        };
16617        self.expect(TokenType::On)?;
16618        let table = self.parse_table_ref()?;
16619
16620        // Optional USING clause
16621        let using = if self.match_token(TokenType::Using) {
16622            Some(self.expect_identifier()?)
16623        } else {
16624            None
16625        };
16626
16627        // Parse index columns (optional for COLUMNSTORE indexes)
16628        let columns = if self.match_token(TokenType::LParen) {
16629            let cols = self.parse_index_columns()?;
16630            self.expect(TokenType::RParen)?;
16631            cols
16632        } else if clustered
16633            .as_ref()
16634            .is_some_and(|c| c.contains("COLUMNSTORE"))
16635        {
16636            // COLUMNSTORE indexes don't require a column list
16637            Vec::new()
16638        } else if matches!(
16639            self.config.dialect,
16640            Some(crate::dialects::DialectType::ClickHouse)
16641        ) {
16642            // ClickHouse: CREATE INDEX idx ON table expr TYPE minmax GRANULARITY 1
16643            // No parentheses around the expression — consume to semicolon as Command
16644            let mut parts = vec![
16645                "CREATE".to_string(),
16646                if unique {
16647                    "UNIQUE INDEX".to_string()
16648                } else {
16649                    "INDEX".to_string()
16650                },
16651                name.name.clone(),
16652                "ON".to_string(),
16653            ];
16654            // Rebuild table name
16655            if let Some(ref s) = table.schema {
16656                parts.push(format!("{}.{}", s.name, table.name.name));
16657            } else {
16658                parts.push(table.name.name.clone());
16659            }
16660            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
16661                let token = self.advance();
16662                if token.token_type == TokenType::String {
16663                    parts.push(format!("'{}'", token.text));
16664                } else if token.token_type == TokenType::QuotedIdentifier {
16665                    parts.push(format!("\"{}\"", token.text));
16666                } else {
16667                    parts.push(token.text.clone());
16668                }
16669            }
16670            return Ok(Expression::Command(Box::new(crate::expressions::Command {
16671                this: parts.join(" "),
16672            })));
16673        } else {
16674            self.expect(TokenType::LParen)?;
16675            let cols = self.parse_index_columns()?;
16676            self.expect(TokenType::RParen)?;
16677            cols
16678        };
16679
16680        // PostgreSQL: INCLUDE (col1, col2) clause
16681        let include_columns = if self.match_identifier("INCLUDE") {
16682            self.expect(TokenType::LParen)?;
16683            let mut cols = Vec::new();
16684            loop {
16685                cols.push(self.expect_identifier_with_quoted()?);
16686                if !self.match_token(TokenType::Comma) {
16687                    break;
16688                }
16689            }
16690            self.expect(TokenType::RParen)?;
16691            cols
16692        } else {
16693            Vec::new()
16694        };
16695
16696        // TSQL: WITH (option=value, ...) clause for index options
16697        let with_options = if self.check(TokenType::With) {
16698            // parse_with_properties expects the WITH keyword to NOT be consumed
16699            // but we need to check if we have WITH followed by LParen
16700            if self
16701                .peek_nth(1)
16702                .is_some_and(|t| t.token_type == TokenType::LParen)
16703            {
16704                self.skip(); // consume WITH
16705                self.parse_with_properties()?
16706            } else {
16707                Vec::new()
16708            }
16709        } else {
16710            Vec::new()
16711        };
16712
16713        // PostgreSQL: WHERE clause for partial indexes
16714        let where_clause = if self.match_token(TokenType::Where) {
16715            Some(Box::new(self.parse_expression()?))
16716        } else {
16717            None
16718        };
16719
16720        // TSQL: ON filegroup or partition scheme clause
16721        // e.g., ON PRIMARY, ON X([y])
16722        let on_filegroup = if self.match_token(TokenType::On) {
16723            // Get the filegroup/partition scheme name
16724            let token = self.advance();
16725            let mut filegroup = token.text.clone();
16726            // Check for partition scheme with column: ON partition_scheme(column)
16727            if self.match_token(TokenType::LParen) {
16728                filegroup.push('(');
16729                // Parse the partition column(s)
16730                loop {
16731                    let col_token = self.advance();
16732                    // For TSQL, use bracket quoting for quoted identifiers
16733                    if col_token.token_type == TokenType::QuotedIdentifier {
16734                        filegroup.push('[');
16735                        filegroup.push_str(&col_token.text);
16736                        filegroup.push(']');
16737                    } else {
16738                        filegroup.push_str(&col_token.text);
16739                    }
16740                    if !self.match_token(TokenType::Comma) {
16741                        break;
16742                    }
16743                    filegroup.push_str(", ");
16744                }
16745                self.expect(TokenType::RParen)?;
16746                filegroup.push(')');
16747            }
16748            Some(filegroup)
16749        } else {
16750            None
16751        };
16752
16753        Ok(Expression::CreateIndex(Box::new(CreateIndex {
16754            name,
16755            table,
16756            columns,
16757            unique,
16758            if_not_exists,
16759            using,
16760            clustered,
16761            concurrently,
16762            where_clause,
16763            include_columns,
16764            with_options,
16765            on_filegroup,
16766        })))
16767    }
16768
16769    /// Parse index columns - can be identifiers or expressions (like function calls)
16770    fn parse_index_columns(&mut self) -> Result<Vec<IndexColumn>> {
16771        let mut columns = Vec::new();
16772        loop {
16773            // Parse as expression to handle function calls like BOX(location, location)
16774            let expr = self.parse_expression()?;
16775
16776            // Extract column name from expression
16777            let column = match &expr {
16778                Expression::Identifier(ident) => ident.clone(),
16779                Expression::Column(col) => {
16780                    // For column expressions (e.g., simple identifier like [Col]),
16781                    // extract the identifier directly to preserve quoting
16782                    col.name.clone()
16783                }
16784                Expression::Function(_func) => {
16785                    // For function expressions, create an identifier from the function call
16786                    Identifier::new(self.expression_to_sql(&expr))
16787                }
16788                _ => Identifier::new(self.expression_to_sql(&expr)),
16789            };
16790
16791            // Parse optional PostgreSQL operator class (e.g., varchar_pattern_ops, public.gin_trgm_ops)
16792            // An opclass is an identifier that appears before ASC/DESC/NULLS and is not a keyword
16793            let opclass = if self.is_identifier_token()
16794                && !self.check(TokenType::Asc)
16795                && !self.check(TokenType::Desc)
16796                && !self.check(TokenType::Nulls)
16797            {
16798                let mut opclass_name = self.advance().text;
16799                // Handle qualified opclass names like public.gin_trgm_ops
16800                while self.match_token(TokenType::Dot) {
16801                    opclass_name.push('.');
16802                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
16803                        opclass_name.push_str(&self.advance().text);
16804                    }
16805                }
16806                Some(opclass_name)
16807            } else {
16808                None
16809            };
16810
16811            let desc = self.match_token(TokenType::Desc);
16812            let asc = if !desc {
16813                self.match_token(TokenType::Asc)
16814            } else {
16815                false
16816            };
16817            let nulls_first = if self.match_token(TokenType::Nulls) {
16818                if self.match_token(TokenType::First) {
16819                    Some(true)
16820                } else if self.match_token(TokenType::Last) {
16821                    Some(false)
16822                } else {
16823                    None
16824                }
16825            } else {
16826                None
16827            };
16828            columns.push(IndexColumn {
16829                column,
16830                desc,
16831                asc,
16832                nulls_first,
16833                opclass,
16834            });
16835            if !self.match_token(TokenType::Comma) {
16836                break;
16837            }
16838        }
16839        Ok(columns)
16840    }
16841
16842    /// Convert an expression to its SQL string representation (simple version for index expressions)
16843    fn expression_to_sql(&self, expr: &Expression) -> String {
16844        match expr {
16845            Expression::Identifier(ident) => ident.name.clone(),
16846            Expression::Function(func) => {
16847                let args = func
16848                    .args
16849                    .iter()
16850                    .map(|a| self.expression_to_sql(a))
16851                    .collect::<Vec<_>>()
16852                    .join(", ");
16853                format!("{}({})", func.name, args)
16854            }
16855            Expression::Column(col) => {
16856                if let Some(ref table) = col.table {
16857                    format!("{}.{}", table, col.name)
16858                } else {
16859                    col.name.to_string()
16860                }
16861            }
16862            Expression::Literal(lit) => match lit.as_ref() {
16863                Literal::String(s) => format!("'{}'", s),
16864                Literal::Number(n) => n.clone(),
16865                _ => "?".to_string(),
16866            },
16867            Expression::Null(_) => "NULL".to_string(),
16868            Expression::Boolean(b) => {
16869                if b.value {
16870                    "TRUE".to_string()
16871                } else {
16872                    "FALSE".to_string()
16873                }
16874            }
16875            _ => "?".to_string(),
16876        }
16877    }
16878
16879    /// Parse DROP statement
16880    fn parse_drop(&mut self) -> Result<Expression> {
16881        // Capture leading comments from the DROP token (e.g., "-- comment\nDROP TABLE ...")
16882        let leading_comments = self.current_leading_comments().to_vec();
16883        self.expect(TokenType::Drop)?;
16884
16885        // ClickHouse: DROP TEMPORARY TABLE / DROP TEMPORARY VIEW
16886        if self.check(TokenType::Temporary)
16887            && matches!(
16888                self.config.dialect,
16889                Some(crate::dialects::DialectType::ClickHouse)
16890            )
16891        {
16892            self.skip(); // consume TEMPORARY
16893            if self.check(TokenType::View) {
16894                return self.parse_drop_view(false);
16895            }
16896            return self.parse_drop_table_with_iceberg(leading_comments.clone(), false);
16897        }
16898
16899        // Snowflake: DROP ICEBERG TABLE
16900        if self.check_identifier("ICEBERG")
16901            && self.current + 1 < self.tokens.len()
16902            && self.tokens[self.current + 1].token_type == TokenType::Table
16903        {
16904            self.skip(); // consume ICEBERG
16905            return self.parse_drop_table_with_iceberg(leading_comments, true);
16906        }
16907
16908        match self.peek().token_type {
16909            TokenType::Table => self.parse_drop_table_with_iceberg(leading_comments, false),
16910            TokenType::View => self.parse_drop_view(false),
16911            TokenType::Materialized => {
16912                self.skip(); // consume MATERIALIZED
16913                self.parse_drop_view(true)
16914            }
16915            TokenType::Index => self.parse_drop_index(),
16916            TokenType::Schema => self.parse_drop_schema(),
16917            TokenType::Database => self.parse_drop_database(),
16918            TokenType::Function => self.parse_drop_function(),
16919            TokenType::Procedure => self.parse_drop_procedure(),
16920            TokenType::Sequence => self.parse_drop_sequence(),
16921            TokenType::Trigger => self.parse_drop_trigger(),
16922            TokenType::Type => self.parse_drop_type(),
16923            TokenType::Domain => {
16924                // DROP DOMAIN is similar to DROP TYPE
16925                self.skip();
16926                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16927                let name = self.parse_table_ref()?;
16928                let cascade = self.match_token(TokenType::Cascade);
16929                if !cascade {
16930                    self.match_token(TokenType::Restrict);
16931                }
16932                Ok(Expression::DropType(Box::new(DropType {
16933                    name,
16934                    if_exists,
16935                    cascade,
16936                })))
16937            }
16938            TokenType::Namespace => {
16939                // DROP NAMESPACE is similar to DROP SCHEMA (Spark/Databricks)
16940                self.skip();
16941                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
16942                // Parse potentially qualified namespace name (a.b.c)
16943                let mut name_parts = vec![self.expect_identifier()?];
16944                while self.match_token(TokenType::Dot) {
16945                    name_parts.push(self.expect_identifier()?);
16946                }
16947                let name = Identifier::new(name_parts.join("."));
16948                let cascade = self.match_token(TokenType::Cascade);
16949                if !cascade {
16950                    self.match_token(TokenType::Restrict);
16951                }
16952                Ok(Expression::DropNamespace(Box::new(DropNamespace {
16953                    name,
16954                    if_exists,
16955                    cascade,
16956                })))
16957            }
16958            _ => {
16959                // ClickHouse: DROP DICTIONARY, DROP USER, DROP QUOTA, DROP ROLE,
16960                // DROP ROW POLICY, DROP SETTINGS PROFILE, DROP NAMED COLLECTION
16961                if matches!(
16962                    self.config.dialect,
16963                    Some(crate::dialects::DialectType::ClickHouse)
16964                ) {
16965                    let text_upper = self.peek().text.to_ascii_uppercase();
16966                    if matches!(
16967                        text_upper.as_str(),
16968                        "DICTIONARY"
16969                            | "USER"
16970                            | "QUOTA"
16971                            | "ROLE"
16972                            | "ROW"
16973                            | "POLICY"
16974                            | "NAMED"
16975                            | "WORKLOAD"
16976                            | "RESOURCE"
16977                            | "PROFILE"
16978                    ) || self.check(TokenType::Settings)
16979                        || self.check(TokenType::Partition)
16980                    {
16981                        self.skip(); // consume keyword, previous() is now set
16982                        let mut tokens: Vec<(String, TokenType)> = vec![
16983                            ("DROP".to_string(), TokenType::Var),
16984                            (
16985                                self.previous().text.to_ascii_uppercase(),
16986                                self.previous().token_type,
16987                            ),
16988                        ];
16989                        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
16990                            let token = self.advance();
16991                            let text = if token.token_type == TokenType::QuotedIdentifier {
16992                                format!("\"{}\"", token.text)
16993                            } else if token.token_type == TokenType::String {
16994                                format!("'{}'", token.text)
16995                            } else {
16996                                token.text.clone()
16997                            };
16998                            tokens.push((text, token.token_type));
16999                        }
17000                        return Ok(Expression::Command(Box::new(Command {
17001                            this: self.join_command_tokens(tokens),
17002                        })));
17003                    }
17004                }
17005                // Snowflake: DROP STREAM, DROP TASK, DROP STAGE, DROP WAREHOUSE, etc.
17006                if matches!(
17007                    self.config.dialect,
17008                    Some(crate::dialects::DialectType::Snowflake)
17009                ) {
17010                    let text_upper = self.peek().text.to_ascii_uppercase();
17011                    let is_snowflake_drop = matches!(
17012                        text_upper.as_str(),
17013                        "STREAM"
17014                            | "TASK"
17015                            | "STAGE"
17016                            | "WAREHOUSE"
17017                            | "PIPE"
17018                            | "INTEGRATION"
17019                            | "TAG"
17020                            | "NETWORK"
17021                            | "SHARE"
17022                    ) || (text_upper == "FILE"
17023                        && self.current + 1 < self.tokens.len()
17024                        && self.tokens[self.current + 1]
17025                            .text
17026                            .eq_ignore_ascii_case("FORMAT"));
17027                    if is_snowflake_drop {
17028                        self.skip(); // consume the object type keyword
17029                        let mut tokens: Vec<(String, TokenType)> = vec![
17030                            ("DROP".to_string(), TokenType::Var),
17031                            (
17032                                self.previous().text.to_ascii_uppercase(),
17033                                self.previous().token_type,
17034                            ),
17035                        ];
17036                        // For FILE FORMAT, also consume FORMAT
17037                        if text_upper == "FILE" {
17038                            let fmt = self.advance();
17039                            tokens.push((fmt.text.to_ascii_uppercase(), fmt.token_type));
17040                        }
17041                        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17042                            let token = self.advance();
17043                            let text = if token.token_type == TokenType::QuotedIdentifier {
17044                                format!("\"{}\"", token.text)
17045                            } else if token.token_type == TokenType::String {
17046                                format!("'{}'", token.text)
17047                            } else {
17048                                token.text.clone()
17049                            };
17050                            tokens.push((text, token.token_type));
17051                        }
17052                        return Ok(Expression::Command(Box::new(Command {
17053                            this: self.join_command_tokens(tokens),
17054                        })));
17055                    }
17056                }
17057                Err(self.parse_error(format!(
17058                    "Expected TABLE, VIEW, INDEX, SCHEMA, DATABASE, FUNCTION, PROCEDURE, SEQUENCE, TRIGGER, TYPE, or NAMESPACE after DROP, got {:?}",
17059                    self.peek().token_type
17060                )))
17061            }
17062        }
17063    }
17064
17065    /// Parse DROP TABLE
17066    fn parse_drop_table_with_iceberg(
17067        &mut self,
17068        leading_comments: Vec<String>,
17069        iceberg: bool,
17070    ) -> Result<Expression> {
17071        self.expect(TokenType::Table)?;
17072
17073        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17074
17075        // ClickHouse: IF EMPTY
17076        if !if_exists
17077            && matches!(
17078                self.config.dialect,
17079                Some(crate::dialects::DialectType::ClickHouse)
17080            )
17081        {
17082            if self.check(TokenType::If)
17083                && self.current + 1 < self.tokens.len()
17084                && self.tokens[self.current + 1]
17085                    .text
17086                    .eq_ignore_ascii_case("EMPTY")
17087            {
17088                self.skip(); // consume IF
17089                self.skip(); // consume EMPTY
17090            }
17091        }
17092
17093        // Parse table names (can be multiple)
17094        let mut names = Vec::new();
17095        loop {
17096            names.push(self.parse_table_ref()?);
17097            if !self.match_token(TokenType::Comma) {
17098                break;
17099            }
17100        }
17101
17102        // Handle CASCADE [CONSTRAINTS] or RESTRICT
17103        let mut cascade = false;
17104        let mut cascade_constraints = false;
17105        let mut restrict = false;
17106        if self.match_token(TokenType::Cascade) {
17107            if self.match_identifier("CONSTRAINTS") {
17108                cascade_constraints = true;
17109            } else {
17110                cascade = true;
17111            }
17112        } else {
17113            restrict = self.match_token(TokenType::Restrict);
17114        }
17115
17116        // Handle PURGE (Oracle)
17117        let purge = self.match_identifier("PURGE");
17118
17119        // ClickHouse: ON CLUSTER clause
17120        if matches!(
17121            self.config.dialect,
17122            Some(crate::dialects::DialectType::ClickHouse)
17123        ) {
17124            let _ = self.parse_on_cluster_clause()?;
17125        }
17126
17127        // ClickHouse: SYNC keyword
17128        let sync = if matches!(
17129            self.config.dialect,
17130            Some(crate::dialects::DialectType::ClickHouse)
17131        ) {
17132            let s = self.match_identifier("SYNC");
17133            self.match_identifier("NO");
17134            self.match_identifier("DELAY");
17135            s
17136        } else {
17137            false
17138        };
17139
17140        Ok(Expression::DropTable(Box::new(DropTable {
17141            names,
17142            if_exists,
17143            cascade,
17144            cascade_constraints,
17145            purge,
17146            leading_comments,
17147            object_id_args: None,
17148            sync,
17149            iceberg,
17150            restrict,
17151        })))
17152    }
17153
17154    /// Parse DROP VIEW
17155    fn parse_drop_view(&mut self, materialized: bool) -> Result<Expression> {
17156        self.expect(TokenType::View)?;
17157
17158        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17159        let name = self.parse_table_ref()?;
17160
17161        // ClickHouse: ON CLUSTER clause
17162        if matches!(
17163            self.config.dialect,
17164            Some(crate::dialects::DialectType::ClickHouse)
17165        ) {
17166            let _ = self.parse_on_cluster_clause()?;
17167            self.match_identifier("SYNC");
17168        }
17169
17170        Ok(Expression::DropView(Box::new(DropView {
17171            name,
17172            if_exists,
17173            materialized,
17174        })))
17175    }
17176
17177    /// Parse DROP INDEX
17178    fn parse_drop_index(&mut self) -> Result<Expression> {
17179        self.expect(TokenType::Index)?;
17180
17181        // PostgreSQL CONCURRENTLY modifier
17182        let concurrently = self.match_identifier("CONCURRENTLY");
17183
17184        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17185
17186        // Parse potentially qualified index name (a.b.c)
17187        let mut name_parts = vec![self.expect_identifier()?];
17188        while self.match_token(TokenType::Dot) {
17189            name_parts.push(self.expect_identifier()?);
17190        }
17191        let name = Identifier::new(name_parts.join("."));
17192
17193        // Optional ON table
17194        let table = if self.match_token(TokenType::On) {
17195            Some(self.parse_table_ref()?)
17196        } else {
17197            None
17198        };
17199
17200        Ok(Expression::DropIndex(Box::new(DropIndex {
17201            name,
17202            table,
17203            if_exists,
17204            concurrently,
17205        })))
17206    }
17207
17208    /// Parse ALTER statement
17209    fn parse_alter(&mut self) -> Result<Expression> {
17210        self.expect(TokenType::Alter)?;
17211
17212        // Check for ICEBERG modifier before TABLE
17213        let alter_table_modifier = if self.check_identifier("ICEBERG") {
17214            self.skip();
17215            Some("ICEBERG".to_string())
17216        } else {
17217            None
17218        };
17219
17220        match self.peek().token_type {
17221            TokenType::Table => {
17222                self.skip();
17223                // Handle IF EXISTS after ALTER TABLE
17224                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17225                // Handle PostgreSQL ONLY modifier: ALTER TABLE ONLY "Album" ...
17226                let has_only = self.match_token(TokenType::Only);
17227                let mut name = self.parse_table_ref()?;
17228                if has_only {
17229                    name.only = true;
17230                }
17231
17232                // ClickHouse: ON CLUSTER clause
17233                let on_cluster = self.parse_on_cluster_clause()?;
17234
17235                // Hive: PARTITION(key=value, ...) clause before actions
17236                let partition = if self.match_token(TokenType::Partition) {
17237                    self.expect(TokenType::LParen)?;
17238                    let mut parts = Vec::new();
17239                    loop {
17240                        let key = self.expect_identifier()?;
17241                        self.expect(TokenType::Eq)?;
17242                        let value = self.parse_expression()?;
17243                        parts.push((Identifier::new(key), value));
17244                        if !self.match_token(TokenType::Comma) {
17245                            break;
17246                        }
17247                    }
17248                    self.expect(TokenType::RParen)?;
17249                    Some(parts)
17250                } else {
17251                    None
17252                };
17253
17254                let mut actions = Vec::new();
17255                let mut last_was_add_column = false;
17256                let mut with_check_modifier: Option<String> = None;
17257
17258                loop {
17259                    // Check for MySQL trailing options (ALGORITHM=val, LOCK=val)
17260                    // before trying to parse as a column def or action.
17261                    // The comma before ALGORITHM was consumed at the bottom of the previous iteration.
17262                    if self.check_identifier("ALGORITHM") || self.check_identifier("LOCK") {
17263                        break;
17264                    }
17265
17266                    // TSQL: WITH CHECK / WITH NOCHECK before ADD CONSTRAINT
17267                    if self.check(TokenType::With) {
17268                        let saved = self.current;
17269                        self.skip(); // consume WITH
17270                        if self.check(TokenType::Check) {
17271                            self.skip(); // consume CHECK
17272                            with_check_modifier = Some("WITH CHECK".to_string());
17273                            // Continue to parse the actual action (ADD CONSTRAINT, etc.)
17274                        } else if self.check_identifier("NOCHECK") {
17275                            self.skip(); // consume NOCHECK
17276                            with_check_modifier = Some("WITH NOCHECK".to_string());
17277                            // Continue to parse the actual action (ADD CONSTRAINT, etc.)
17278                        } else {
17279                            // Not WITH CHECK/NOCHECK, restore position
17280                            self.current = saved;
17281                        }
17282                    }
17283
17284                    // If last action was ADD COLUMN and we just saw a comma,
17285                    // check if this is another column definition (not a new action keyword)
17286                    if last_was_add_column
17287                        && !self.check(TokenType::Add)
17288                        && !self.check(TokenType::Drop)
17289                        && !self.check(TokenType::Alter)
17290                        && !self.check(TokenType::Rename)
17291                        && !self.check(TokenType::Set)
17292                        && !self.check_identifier("MODIFY")
17293                        && !self.check(TokenType::Delete)
17294                        && !self.check(TokenType::Update)
17295                        && !self.check_identifier("DETACH")
17296                        && !self.check_identifier("ATTACH")
17297                        && !self.check_identifier("FREEZE")
17298                        && !self.check_identifier("CLEAR")
17299                        && !self.check_identifier("MATERIALIZE")
17300                        && !self.check(TokenType::Comment)
17301                        && !self.check(TokenType::Replace)
17302                        && !self.check_identifier("MOVE")
17303                        && !self.check_identifier("REMOVE")
17304                        && !self.check_identifier("APPLY")
17305                    {
17306                        // Parse additional column definition
17307                        self.match_token(TokenType::Column); // optional COLUMN keyword
17308                        let if_not_exists = self.match_keywords(&[
17309                            TokenType::If,
17310                            TokenType::Not,
17311                            TokenType::Exists,
17312                        ]);
17313                        let col_def = self.parse_column_def()?;
17314                        let position = if self.match_token(TokenType::First) {
17315                            Some(ColumnPosition::First)
17316                        } else if self.match_token(TokenType::After) {
17317                            let after_col = self.expect_identifier()?;
17318                            // ClickHouse: AFTER n.a (dotted nested column name)
17319                            let after_name = if self.match_token(TokenType::Dot) {
17320                                let field = self.expect_identifier()?;
17321                                format!("{}.{}", after_col, field)
17322                            } else {
17323                                after_col
17324                            };
17325                            Some(ColumnPosition::After(Identifier::new(after_name)))
17326                        } else {
17327                            None
17328                        };
17329                        actions.push(AlterTableAction::AddColumn {
17330                            column: col_def,
17331                            if_not_exists,
17332                            position,
17333                        });
17334                        // last_was_add_column remains true
17335                    } else {
17336                        // Check for MySQL trailing options (ALGORITHM=val, LOCK=val)
17337                        // before trying to parse as an action
17338                        if self.check_identifier("ALGORITHM") || self.check_identifier("LOCK") {
17339                            // Retreat one to re-process the comma in the trailing options loop
17340                            self.current -= 1; // back up past the comma consumed in loop
17341                            break;
17342                        }
17343                        let action = self.parse_alter_action()?;
17344                        last_was_add_column = matches!(action, AlterTableAction::AddColumn { .. });
17345                        actions.push(action);
17346                    }
17347                    if !self.match_token(TokenType::Comma) {
17348                        break;
17349                    }
17350                }
17351
17352                // Parse trailing MySQL ALTER TABLE options: ALGORITHM=val, LOCK=val
17353                // These can appear after actions separated by commas (comma already consumed)
17354                // or directly if no actions were parsed
17355                let mut algorithm = None;
17356                let mut lock = None;
17357                loop {
17358                    // First check without consuming comma (comma may have been consumed by action loop)
17359                    if self.check_identifier("ALGORITHM") {
17360                        self.skip();
17361                        self.expect(TokenType::Eq)?;
17362                        algorithm = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17363                        self.match_token(TokenType::Comma); // optional trailing comma
17364                    } else if self.check_identifier("LOCK") {
17365                        self.skip();
17366                        self.expect(TokenType::Eq)?;
17367                        lock = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17368                        self.match_token(TokenType::Comma); // optional trailing comma
17369                    } else if self.match_token(TokenType::Comma) {
17370                        // Try after comma
17371                        if self.check_identifier("ALGORITHM") {
17372                            self.skip();
17373                            self.expect(TokenType::Eq)?;
17374                            algorithm =
17375                                Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17376                        } else if self.check_identifier("LOCK") {
17377                            self.skip();
17378                            self.expect(TokenType::Eq)?;
17379                            lock = Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17380                        } else {
17381                            self.current -= 1;
17382                            break;
17383                        }
17384                    } else {
17385                        break;
17386                    }
17387                }
17388
17389                // ClickHouse: consume optional trailing SETTINGS clause
17390                // e.g., ALTER TABLE t ADD COLUMN c Int64 SETTINGS mutations_sync=2, alter_sync=2
17391                if matches!(
17392                    self.config.dialect,
17393                    Some(crate::dialects::DialectType::ClickHouse)
17394                ) && self.check(TokenType::Settings)
17395                {
17396                    self.skip(); // consume SETTINGS
17397                    let _ = self.parse_settings_property()?;
17398                }
17399
17400                Ok(Expression::AlterTable(Box::new(AlterTable {
17401                    name,
17402                    actions,
17403                    if_exists,
17404                    algorithm,
17405                    lock,
17406                    with_check: with_check_modifier,
17407                    partition,
17408                    on_cluster,
17409                    table_modifier: alter_table_modifier,
17410                })))
17411            }
17412            TokenType::View => self.parse_alter_view_with_modifiers(None, None, None),
17413            TokenType::Index => self.parse_alter_index(),
17414            TokenType::Sequence => self.parse_alter_sequence(),
17415            _ if self.check_identifier("SESSION") => {
17416                // ALTER SESSION SET/UNSET (Snowflake)
17417                self.skip(); // consume SESSION
17418                match self.parse_alter_session()? {
17419                    Some(expr) => Ok(expr),
17420                    None => {
17421                        // Fall back to command
17422                        Ok(Expression::Command(Box::new(Command {
17423                            this: "ALTER SESSION".to_string(),
17424                        })))
17425                    }
17426                }
17427            }
17428            _ => {
17429                // MySQL: ALTER ALGORITHM = val VIEW, ALTER DEFINER = val VIEW,
17430                // ALTER SQL SECURITY = val VIEW
17431                let mut view_algorithm = None;
17432                let mut view_definer = None;
17433                let mut view_sql_security = None;
17434
17435                loop {
17436                    if self.check_identifier("ALGORITHM") {
17437                        self.skip();
17438                        self.expect(TokenType::Eq)?;
17439                        view_algorithm =
17440                            Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17441                    } else if self.check_identifier("DEFINER") {
17442                        self.skip();
17443                        self.expect(TokenType::Eq)?;
17444                        // Parse user@host format: 'admin'@'localhost'
17445                        let mut definer_str = String::new();
17446                        if self.check(TokenType::String) {
17447                            definer_str.push_str(&format!("'{}'", self.advance().text));
17448                        } else {
17449                            definer_str.push_str(&self.expect_identifier_or_keyword()?);
17450                        }
17451                        // Check for @ separator
17452                        if !self.is_at_end() && self.peek().text == "@" {
17453                            definer_str.push_str(&self.advance().text);
17454                            if self.check(TokenType::String) {
17455                                definer_str.push_str(&format!("'{}'", self.advance().text));
17456                            } else if !self.is_at_end() {
17457                                definer_str.push_str(&self.advance().text);
17458                            }
17459                        }
17460                        view_definer = Some(definer_str);
17461                    } else if self.check_identifier("SQL") {
17462                        self.skip();
17463                        if self.match_identifier("SECURITY") {
17464                            self.match_token(TokenType::Eq);
17465                            view_sql_security =
17466                                Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase());
17467                        }
17468                    } else {
17469                        break;
17470                    }
17471                }
17472
17473                if self.check(TokenType::View) {
17474                    self.parse_alter_view_with_modifiers(
17475                        view_algorithm,
17476                        view_definer,
17477                        view_sql_security,
17478                    )
17479                } else {
17480                    // Fall back to Raw for unrecognized ALTER targets
17481                    let start = self.current;
17482                    while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17483                        self.skip();
17484                    }
17485                    let sql = self.tokens_to_sql(start, self.current);
17486                    Ok(Expression::Raw(Raw {
17487                        sql: format!("ALTER {}", sql),
17488                    }))
17489                }
17490            }
17491        }
17492    }
17493
17494    /// Parse ALTER TABLE action
17495    fn parse_alter_action(&mut self) -> Result<AlterTableAction> {
17496        if self.match_token(TokenType::Add) {
17497            // ClickHouse: ADD INDEX idx expr TYPE minmax GRANULARITY 1
17498            // ClickHouse: ADD PROJECTION name (SELECT ...)
17499            // ClickHouse: ADD STATISTICS col1, col2 TYPE tdigest, uniq
17500            // These have different syntax from MySQL ADD INDEX, so consume as Raw
17501            if matches!(
17502                self.config.dialect,
17503                Some(crate::dialects::DialectType::ClickHouse)
17504            ) && (self.check(TokenType::Index)
17505                || self.check_identifier("PROJECTION")
17506                || self.check_identifier("STATISTICS"))
17507            {
17508                let is_statistics = self.check_identifier("STATISTICS");
17509                let mut tokens: Vec<(String, TokenType)> =
17510                    vec![("ADD".to_string(), TokenType::Add)];
17511                let mut paren_depth = 0i32;
17512                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17513                    // STATISTICS uses commas internally (col1, col2 TYPE t1, t2), don't break at comma
17514                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_statistics {
17515                        break;
17516                    }
17517                    let token = self.advance();
17518                    if token.token_type == TokenType::LParen {
17519                        paren_depth += 1;
17520                    }
17521                    if token.token_type == TokenType::RParen {
17522                        paren_depth -= 1;
17523                    }
17524                    let text = if token.token_type == TokenType::QuotedIdentifier {
17525                        format!("\"{}\"", token.text)
17526                    } else if token.token_type == TokenType::String {
17527                        format!("'{}'", token.text)
17528                    } else {
17529                        token.text.clone()
17530                    };
17531                    tokens.push((text, token.token_type));
17532                }
17533                return Ok(AlterTableAction::Raw {
17534                    sql: self.join_command_tokens(tokens),
17535                });
17536            }
17537            // ADD CONSTRAINT or ADD COLUMN or ADD INDEX
17538            if self.match_token(TokenType::Constraint) {
17539                // ADD CONSTRAINT name ...
17540                let name = Some(self.expect_identifier_with_quoted()?);
17541                let constraint = self.parse_constraint_definition(name)?;
17542                Ok(AlterTableAction::AddConstraint(constraint))
17543            } else if self.check(TokenType::PrimaryKey)
17544                || self.check(TokenType::ForeignKey)
17545                || self.check(TokenType::Check)
17546            {
17547                // ADD PRIMARY KEY / FOREIGN KEY / CHECK (without CONSTRAINT keyword)
17548                let constraint = self.parse_table_constraint()?;
17549                Ok(AlterTableAction::AddConstraint(constraint))
17550            } else if self.check(TokenType::Index)
17551                || self.check(TokenType::Key)
17552                || self.check(TokenType::Unique)
17553                || self.check_identifier("FULLTEXT")
17554                || self.check_identifier("SPATIAL")
17555            {
17556                // ADD [UNIQUE|FULLTEXT|SPATIAL] [{INDEX|KEY}] [name] (columns) [USING {BTREE|HASH}]
17557                let kind = if self.match_token(TokenType::Unique) {
17558                    Some("UNIQUE".to_string())
17559                } else if self.match_identifier("FULLTEXT") {
17560                    Some("FULLTEXT".to_string())
17561                } else if self.match_identifier("SPATIAL") {
17562                    Some("SPATIAL".to_string())
17563                } else {
17564                    None
17565                };
17566                // Consume optional INDEX or KEY keyword, track which was used
17567                let use_key_keyword = if self.match_token(TokenType::Key) {
17568                    true
17569                } else {
17570                    self.match_token(TokenType::Index);
17571                    false
17572                };
17573
17574                // Optional index name (before the columns)
17575                let name = if !self.check(TokenType::LParen) && !self.check(TokenType::Using) {
17576                    Some(self.expect_identifier_with_quoted()?)
17577                } else {
17578                    None
17579                };
17580
17581                // Parse columns (with optional prefix length and DESC)
17582                self.expect(TokenType::LParen)?;
17583                let columns = self.parse_index_identifier_list()?;
17584                self.expect(TokenType::RParen)?;
17585
17586                // Parse optional USING BTREE|HASH
17587                let modifiers = self.parse_constraint_modifiers();
17588
17589                Ok(AlterTableAction::AddConstraint(TableConstraint::Index {
17590                    name,
17591                    columns,
17592                    kind,
17593                    modifiers,
17594                    use_key_keyword,
17595                    expression: None,
17596                    index_type: None,
17597                    granularity: None,
17598                }))
17599            } else if self.match_identifier("COLUMNS") {
17600                // ADD COLUMNS (col1 TYPE, col2 TYPE, ...) [CASCADE] - Hive/Spark syntax
17601                self.expect(TokenType::LParen)?;
17602                let mut columns = Vec::new();
17603                loop {
17604                    let col_def = self.parse_column_def()?;
17605                    columns.push(col_def);
17606                    if !self.match_token(TokenType::Comma) {
17607                        break;
17608                    }
17609                }
17610                self.expect(TokenType::RParen)?;
17611                let cascade = self.match_token(TokenType::Cascade);
17612                Ok(AlterTableAction::AddColumns { columns, cascade })
17613            } else if self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]) {
17614                // ADD IF NOT EXISTS PARTITION(key = value) - Hive/Spark syntax
17615                // ADD IF NOT EXISTS col1 INT, col2 INT - Snowflake syntax
17616                if self.match_token(TokenType::Partition) {
17617                    self.expect(TokenType::LParen)?;
17618                    let mut partition_exprs = Vec::new();
17619                    loop {
17620                        if let Some(expr) = self.parse_conjunction()? {
17621                            partition_exprs.push(expr);
17622                        }
17623                        if !self.match_token(TokenType::Comma) {
17624                            break;
17625                        }
17626                    }
17627                    self.expect(TokenType::RParen)?;
17628                    let partition =
17629                        Expression::Partition(Box::new(crate::expressions::Partition {
17630                            expressions: partition_exprs,
17631                            subpartition: false,
17632                        }));
17633                    let location = if self.match_text_seq(&["LOCATION"]) {
17634                        self.parse_property()?
17635                    } else {
17636                        None
17637                    };
17638                    return Ok(AlterTableAction::AddPartition {
17639                        partition,
17640                        if_not_exists: true,
17641                        location,
17642                    });
17643                } else {
17644                    // Snowflake: ADD IF NOT EXISTS col1 INT, [IF NOT EXISTS] col2 INT
17645                    // Parse just the first column; the caller's comma loop handles the rest
17646                    let col_def = self.parse_column_def()?;
17647                    return Ok(AlterTableAction::AddColumn {
17648                        column: col_def,
17649                        if_not_exists: true,
17650                        position: None,
17651                    });
17652                }
17653            } else if self.check(TokenType::Partition) {
17654                // ADD PARTITION(key = value) - Hive/Spark syntax
17655                self.skip(); // consume PARTITION
17656                self.expect(TokenType::LParen)?;
17657                let mut partition_exprs = Vec::new();
17658                loop {
17659                    if let Some(expr) = self.parse_conjunction()? {
17660                        partition_exprs.push(expr);
17661                    }
17662                    if !self.match_token(TokenType::Comma) {
17663                        break;
17664                    }
17665                }
17666                self.expect(TokenType::RParen)?;
17667                let partition = Expression::Partition(Box::new(crate::expressions::Partition {
17668                    expressions: partition_exprs,
17669                    subpartition: false,
17670                }));
17671                let location = if self.match_text_seq(&["LOCATION"]) {
17672                    // Parse the LOCATION value (typically a string literal like 'path')
17673                    Some(self.parse_primary()?)
17674                } else {
17675                    None
17676                };
17677                Ok(AlterTableAction::AddPartition {
17678                    partition,
17679                    if_not_exists: false,
17680                    location,
17681                })
17682            } else {
17683                // ADD COLUMN or ADD (col1 TYPE, col2 TYPE) for Oracle
17684                let has_column_keyword = self.match_token(TokenType::Column); // optional COLUMN keyword
17685
17686                // Check for Oracle-style ADD (col1 TYPE, col2 TYPE, ...) without COLUMN keyword
17687                if !has_column_keyword && self.check(TokenType::LParen) {
17688                    // Oracle multi-column ADD syntax: ADD (col1 TYPE, col2 TYPE, ...)
17689                    self.skip(); // consume '('
17690                    let mut columns = Vec::new();
17691                    loop {
17692                        let col_def = self.parse_column_def()?;
17693                        columns.push(col_def);
17694                        if !self.match_token(TokenType::Comma) {
17695                            break;
17696                        }
17697                    }
17698                    self.expect(TokenType::RParen)?;
17699                    // Use AddColumns with cascade=false for Oracle syntax
17700                    Ok(AlterTableAction::AddColumns {
17701                        columns,
17702                        cascade: false,
17703                    })
17704                } else {
17705                    // Handle IF NOT EXISTS for ADD COLUMN
17706                    let if_not_exists =
17707                        self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
17708                    let col_def = self.parse_column_def()?;
17709                    // Check for FIRST or AFTER position modifiers (MySQL/MariaDB)
17710                    let position = if self.match_token(TokenType::First) {
17711                        Some(ColumnPosition::First)
17712                    } else if self.match_token(TokenType::After) {
17713                        let after_col = self.expect_identifier()?;
17714                        // ClickHouse: AFTER n.a (dotted nested column name)
17715                        let after_name = if self.match_token(TokenType::Dot) {
17716                            let field = self.expect_identifier()?;
17717                            format!("{}.{}", after_col, field)
17718                        } else {
17719                            after_col
17720                        };
17721                        Some(ColumnPosition::After(Identifier::new(after_name)))
17722                    } else {
17723                        None
17724                    };
17725                    Ok(AlterTableAction::AddColumn {
17726                        column: col_def,
17727                        if_not_exists,
17728                        position,
17729                    })
17730                }
17731            }
17732        } else if self.match_token(TokenType::Drop) {
17733            // ClickHouse: DROP INDEX idx, DROP PROJECTION name, DROP STATISTICS, etc.
17734            // These have different syntax from MySQL, so consume as Raw
17735            if matches!(
17736                self.config.dialect,
17737                Some(crate::dialects::DialectType::ClickHouse)
17738            ) && (self.check(TokenType::Index)
17739                || self.check_identifier("PROJECTION")
17740                || self.check_identifier("STATISTICS")
17741                || self.check_identifier("DETACHED")
17742                || self.check_identifier("PART"))
17743            {
17744                let is_statistics = self.check_identifier("STATISTICS");
17745                let mut tokens: Vec<(String, TokenType)> =
17746                    vec![("DROP".to_string(), TokenType::Drop)];
17747                let mut paren_depth = 0i32;
17748                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
17749                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_statistics {
17750                        break;
17751                    }
17752                    let token = self.advance();
17753                    if token.token_type == TokenType::LParen {
17754                        paren_depth += 1;
17755                    }
17756                    if token.token_type == TokenType::RParen {
17757                        paren_depth -= 1;
17758                    }
17759                    let text = if token.token_type == TokenType::QuotedIdentifier {
17760                        format!("\"{}\"", token.text)
17761                    } else if token.token_type == TokenType::String {
17762                        format!("'{}'", token.text)
17763                    } else {
17764                        token.text.clone()
17765                    };
17766                    tokens.push((text, token.token_type));
17767                }
17768                return Ok(AlterTableAction::Raw {
17769                    sql: self.join_command_tokens(tokens),
17770                });
17771            }
17772            // Handle IF EXISTS before determining what to drop
17773            let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17774
17775            if self.match_token(TokenType::Partition) {
17776                // DROP [IF EXISTS] PARTITION expr [, PARTITION expr ...]
17777                // ClickHouse supports: PARTITION 201901, PARTITION ALL,
17778                // PARTITION tuple(...), PARTITION ID '...'
17779                let mut partitions = Vec::new();
17780                loop {
17781                    if self.check(TokenType::LParen) {
17782                        // ClickHouse: PARTITION (expr) or PARTITION (expr, expr, ...)
17783                        // Standard SQL: PARTITION (key=value, ...)
17784                        // Peek ahead: if LParen is followed by String/Number (not identifier=),
17785                        // parse as expression
17786                        let is_ch_expr = matches!(
17787                            self.config.dialect,
17788                            Some(crate::dialects::DialectType::ClickHouse)
17789                        ) && self.current + 1 < self.tokens.len()
17790                            && (self.tokens[self.current + 1].token_type == TokenType::String
17791                                || self.tokens[self.current + 1].token_type == TokenType::Number
17792                                || self.tokens[self.current + 1].token_type == TokenType::LParen
17793                                || (self.current + 2 < self.tokens.len()
17794                                    && self.tokens[self.current + 2].token_type != TokenType::Eq));
17795                        if is_ch_expr {
17796                            // Parse as tuple expression
17797                            let expr = self.parse_expression()?;
17798                            partitions.push(vec![(Identifier::new("__expr__".to_string()), expr)]);
17799                        } else {
17800                            self.skip(); // consume (
17801                            let mut parts = Vec::new();
17802                            loop {
17803                                let key = self.expect_identifier()?;
17804                                self.expect(TokenType::Eq)?;
17805                                let value = self.parse_expression()?;
17806                                parts.push((Identifier::new(key), value));
17807                                if !self.match_token(TokenType::Comma) {
17808                                    break;
17809                                }
17810                            }
17811                            self.expect(TokenType::RParen)?;
17812                            partitions.push(parts);
17813                        }
17814                    } else if self.match_text_seq(&["ALL"]) {
17815                        // ClickHouse: PARTITION ALL
17816                        partitions.push(vec![(
17817                            Identifier::new("ALL".to_string()),
17818                            Expression::Boolean(BooleanLiteral { value: true }),
17819                        )]);
17820                    } else if self.match_text_seq(&["ID"]) {
17821                        // ClickHouse: PARTITION ID 'string'
17822                        let id_val = self.parse_expression()?;
17823                        partitions.push(vec![(Identifier::new("ID".to_string()), id_val)]);
17824                    } else {
17825                        // ClickHouse: PARTITION <expression> (number, tuple(...), etc.)
17826                        let expr = self.parse_expression()?;
17827                        partitions.push(vec![(Identifier::new("__expr__".to_string()), expr)]);
17828                    }
17829                    // Check for ", PARTITION" for multiple partitions
17830                    if self.match_token(TokenType::Comma) {
17831                        if !self.match_token(TokenType::Partition) {
17832                            break;
17833                        }
17834                    } else {
17835                        break;
17836                    }
17837                }
17838                Ok(AlterTableAction::DropPartition {
17839                    partitions,
17840                    if_exists,
17841                })
17842            } else if self.match_token(TokenType::Column) {
17843                // DROP [IF EXISTS] COLUMN [IF EXISTS] name [CASCADE]
17844                // Check for IF EXISTS after COLUMN as well
17845                let if_exists =
17846                    if_exists || self.match_keywords(&[TokenType::If, TokenType::Exists]);
17847                let mut name = self.expect_identifier_with_quoted()?;
17848                // ClickHouse: nested column names like n.ui8
17849                if matches!(
17850                    self.config.dialect,
17851                    Some(crate::dialects::DialectType::ClickHouse)
17852                ) && self.match_token(TokenType::Dot)
17853                {
17854                    let sub = self.expect_identifier_with_quoted()?;
17855                    name.name = format!("{}.{}", name.name, sub.name);
17856                }
17857                let cascade = self.match_token(TokenType::Cascade);
17858                Ok(AlterTableAction::DropColumn {
17859                    name,
17860                    if_exists,
17861                    cascade,
17862                })
17863            } else if self.match_token(TokenType::Constraint) {
17864                // DROP [IF EXISTS] CONSTRAINT name
17865                let name = self.expect_identifier_with_quoted()?;
17866                Ok(AlterTableAction::DropConstraint { name, if_exists })
17867            } else if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
17868                // DROP FOREIGN KEY name (Oracle/MySQL)
17869                let name = self.expect_identifier_with_quoted()?;
17870                Ok(AlterTableAction::DropForeignKey { name })
17871            } else if self.check_identifier("COLUMNS") && self.check_next(TokenType::LParen) {
17872                // DROP COLUMNS (col1, col2, ...) - Spark/Databricks syntax
17873                self.skip(); // consume COLUMNS
17874                self.expect(TokenType::LParen)?;
17875                let mut names = Vec::new();
17876                loop {
17877                    let name = self.expect_identifier_with_quoted()?;
17878                    names.push(name);
17879                    if !self.match_token(TokenType::Comma) {
17880                        break;
17881                    }
17882                }
17883                self.expect(TokenType::RParen)?;
17884                Ok(AlterTableAction::DropColumns { names })
17885            } else {
17886                // DROP [IF EXISTS] name (implicit column) [CASCADE]
17887                let mut name = self.expect_identifier_with_quoted()?;
17888                // ClickHouse: nested column names like n.ui8
17889                if matches!(
17890                    self.config.dialect,
17891                    Some(crate::dialects::DialectType::ClickHouse)
17892                ) && self.match_token(TokenType::Dot)
17893                {
17894                    let sub = self.expect_identifier_with_quoted()?;
17895                    name.name = format!("{}.{}", name.name, sub.name);
17896                }
17897                let cascade = self.match_token(TokenType::Cascade);
17898                Ok(AlterTableAction::DropColumn {
17899                    name,
17900                    if_exists,
17901                    cascade,
17902                })
17903            }
17904        } else if self.match_token(TokenType::Rename) {
17905            if self.match_token(TokenType::Column) {
17906                // RENAME COLUMN [IF EXISTS] old TO new
17907                let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
17908                let mut old_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
17909                // ClickHouse: nested column names like n.x
17910                if matches!(
17911                    self.config.dialect,
17912                    Some(crate::dialects::DialectType::ClickHouse)
17913                ) && self.match_token(TokenType::Dot)
17914                {
17915                    let field = self.expect_identifier_with_quoted()?;
17916                    old_name = Identifier {
17917                        name: format!("{}.{}", old_name.name, field.name),
17918                        quoted: false,
17919                        trailing_comments: Vec::new(),
17920                        span: None,
17921                    };
17922                }
17923                self.expect(TokenType::To)?;
17924                let mut new_name = self.expect_identifier_or_safe_keyword_with_quoted()?;
17925                // ClickHouse: nested column names like n.y
17926                if matches!(
17927                    self.config.dialect,
17928                    Some(crate::dialects::DialectType::ClickHouse)
17929                ) && self.match_token(TokenType::Dot)
17930                {
17931                    let field = self.expect_identifier_or_safe_keyword_with_quoted()?;
17932                    new_name = Identifier {
17933                        name: format!("{}.{}", new_name.name, field.name),
17934                        quoted: false,
17935                        trailing_comments: Vec::new(),
17936                        span: None,
17937                    };
17938                }
17939                Ok(AlterTableAction::RenameColumn {
17940                    old_name,
17941                    new_name,
17942                    if_exists,
17943                })
17944            } else if self.match_token(TokenType::To) {
17945                // RENAME TO new_table
17946                let new_name = self.parse_table_ref()?;
17947                Ok(AlterTableAction::RenameTable(new_name))
17948            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
17949                // StarRocks/Doris: RENAME new_name (without TO)
17950                // SQLite: RENAME old_name TO new_name (without COLUMN keyword)
17951                let first_name = self.expect_identifier_with_quoted()?;
17952                if self.match_token(TokenType::To) {
17953                    let new_name = self.expect_identifier_with_quoted()?;
17954                    Ok(AlterTableAction::RenameColumn {
17955                        old_name: first_name,
17956                        new_name,
17957                        if_exists: false,
17958                    })
17959                } else {
17960                    // No TO keyword: treat as RENAME TABLE (StarRocks/Doris)
17961                    Ok(AlterTableAction::RenameTable(TableRef::new(
17962                        first_name.name,
17963                    )))
17964                }
17965            } else {
17966                Err(self.parse_error("Expected COLUMN or TO after RENAME"))
17967            }
17968        } else if self.match_token(TokenType::Alter) {
17969            // Check for ALTER INDEX (MySQL: ALTER TABLE t ALTER INDEX i VISIBLE/INVISIBLE)
17970            if self.match_token(TokenType::Index) {
17971                let name = self.expect_identifier_with_quoted()?;
17972                let visible = if self.match_identifier("VISIBLE") {
17973                    true
17974                } else if self.match_identifier("INVISIBLE") {
17975                    false
17976                } else {
17977                    return Err(
17978                        self.parse_error("Expected VISIBLE or INVISIBLE after ALTER INDEX name")
17979                    );
17980                };
17981                Ok(AlterTableAction::AlterIndex { name, visible })
17982            } else if self.check_identifier("SORTKEY") {
17983                // Redshift: ALTER TABLE t ALTER SORTKEY AUTO|NONE|(col1, col2)
17984                self.skip(); // consume SORTKEY
17985                if self.match_texts(&["AUTO", "NONE"]) {
17986                    let style = self.previous().text.to_ascii_uppercase();
17987                    Ok(AlterTableAction::AlterSortKey {
17988                        this: Some(style),
17989                        expressions: Vec::new(),
17990                        compound: false,
17991                    })
17992                } else if self.check(TokenType::LParen) {
17993                    // (col1, col2) syntax
17994                    let wrapped = self.parse_wrapped_id_vars()?;
17995                    let expressions = if let Some(Expression::Tuple(t)) = wrapped {
17996                        t.expressions
17997                    } else {
17998                        Vec::new()
17999                    };
18000                    Ok(AlterTableAction::AlterSortKey {
18001                        this: None,
18002                        expressions,
18003                        compound: false,
18004                    })
18005                } else {
18006                    Err(self.parse_error("Expected AUTO, NONE, or (columns) after SORTKEY"))
18007                }
18008            } else if self.check_identifier("COMPOUND") {
18009                // Redshift: ALTER TABLE t ALTER COMPOUND SORTKEY (col1, col2)
18010                self.skip(); // consume COMPOUND
18011                if !self.match_identifier("SORTKEY") {
18012                    return Err(self.parse_error("Expected SORTKEY after COMPOUND"));
18013                }
18014                if self.check(TokenType::LParen) {
18015                    let wrapped = self.parse_wrapped_id_vars()?;
18016                    let expressions = if let Some(Expression::Tuple(t)) = wrapped {
18017                        t.expressions
18018                    } else {
18019                        Vec::new()
18020                    };
18021                    Ok(AlterTableAction::AlterSortKey {
18022                        this: None,
18023                        expressions,
18024                        compound: true,
18025                    })
18026                } else {
18027                    Err(self.parse_error("Expected (columns) after COMPOUND SORTKEY"))
18028                }
18029            } else if self.check_identifier("DISTSTYLE") {
18030                // Redshift: ALTER TABLE t ALTER DISTSTYLE ALL|EVEN|AUTO|KEY [DISTKEY col]
18031                self.skip(); // consume DISTSTYLE
18032                if self.match_texts(&["ALL", "EVEN", "AUTO"]) {
18033                    let style = self.previous().text.to_ascii_uppercase();
18034                    Ok(AlterTableAction::AlterDistStyle {
18035                        style,
18036                        distkey: None,
18037                    })
18038                } else if self.match_token(TokenType::Key) || self.match_identifier("KEY") {
18039                    // DISTSTYLE KEY DISTKEY col
18040                    if !self.match_identifier("DISTKEY") {
18041                        return Err(self.parse_error("Expected DISTKEY after DISTSTYLE KEY"));
18042                    }
18043                    let col = self.expect_identifier_with_quoted()?;
18044                    Ok(AlterTableAction::AlterDistStyle {
18045                        style: "KEY".to_string(),
18046                        distkey: Some(col),
18047                    })
18048                } else {
18049                    Err(self.parse_error("Expected ALL, EVEN, AUTO, or KEY after DISTSTYLE"))
18050                }
18051            } else if self.check_identifier("DISTKEY") {
18052                // Redshift: ALTER TABLE t ALTER DISTKEY col (shorthand for DISTSTYLE KEY DISTKEY col)
18053                self.skip(); // consume DISTKEY
18054                let col = self.expect_identifier_with_quoted()?;
18055                Ok(AlterTableAction::AlterDistStyle {
18056                    style: "KEY".to_string(),
18057                    distkey: Some(col),
18058                })
18059            } else {
18060                // ALTER COLUMN
18061                self.match_token(TokenType::Column); // optional COLUMN keyword
18062                let name = self.expect_identifier_with_quoted()?;
18063                let action = self.parse_alter_column_action()?;
18064                Ok(AlterTableAction::AlterColumn {
18065                    name,
18066                    action,
18067                    use_modify_keyword: false,
18068                })
18069            }
18070        } else if self.match_identifier("MODIFY") {
18071            // ClickHouse: MODIFY ORDER BY, MODIFY SETTING, MODIFY TTL, MODIFY QUERY,
18072            // MODIFY COLUMN name type [DEFAULT|MATERIALIZED|ALIAS] [CODEC] [TTL] [COMMENT], etc.
18073            // These are ClickHouse-specific and have richer syntax than MySQL MODIFY COLUMN.
18074            // Consume all ClickHouse MODIFY actions as Raw.
18075            if matches!(
18076                self.config.dialect,
18077                Some(crate::dialects::DialectType::ClickHouse)
18078            ) {
18079                // MODIFY SETTING uses commas between settings (not action separators)
18080                let is_setting =
18081                    self.check(TokenType::Settings) || self.check_identifier("SETTING");
18082                let mut tokens: Vec<(String, TokenType)> =
18083                    vec![("MODIFY".to_string(), TokenType::Var)];
18084                let mut paren_depth = 0i32;
18085                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18086                    if self.check(TokenType::Comma) && paren_depth == 0 && !is_setting {
18087                        break;
18088                    }
18089                    let token = self.advance();
18090                    if token.token_type == TokenType::LParen {
18091                        paren_depth += 1;
18092                    }
18093                    if token.token_type == TokenType::RParen {
18094                        paren_depth -= 1;
18095                    }
18096                    let text = if token.token_type == TokenType::QuotedIdentifier {
18097                        format!("\"{}\"", token.text)
18098                    } else if token.token_type == TokenType::String {
18099                        format!("'{}'", token.text)
18100                    } else {
18101                        token.text.clone()
18102                    };
18103                    tokens.push((text, token.token_type));
18104                }
18105                return Ok(AlterTableAction::Raw {
18106                    sql: self.join_command_tokens(tokens),
18107                });
18108            }
18109            // MODIFY COLUMN (MySQL syntax for altering column type)
18110            self.match_token(TokenType::Column); // optional COLUMN keyword
18111            let name = Identifier::new(self.expect_identifier()?);
18112            // Parse the data type directly (MySQL MODIFY COLUMN col TYPE)
18113            let data_type = self.parse_data_type()?;
18114            // Parse optional COLLATE clause
18115            let collate = if self.match_token(TokenType::Collate) {
18116                if self.check(TokenType::String) {
18117                    Some(self.advance().text)
18118                } else if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
18119                    Some(self.advance().text)
18120                } else {
18121                    None
18122                }
18123            } else {
18124                None
18125            };
18126            Ok(AlterTableAction::AlterColumn {
18127                name,
18128                action: AlterColumnAction::SetDataType {
18129                    data_type,
18130                    using: None,
18131                    collate,
18132                },
18133                use_modify_keyword: true,
18134            })
18135        } else if self.match_identifier("CHANGE") {
18136            // CHANGE [COLUMN] old_name new_name [data_type] [COMMENT 'comment'] - Hive/MySQL/SingleStore syntax
18137            // In SingleStore, data_type can be omitted for simple renames
18138            self.match_token(TokenType::Column); // optional COLUMN keyword
18139            let old_name = Identifier::new(self.expect_identifier()?);
18140            let new_name = Identifier::new(self.expect_identifier()?);
18141            // Try to parse data type - it's optional in SingleStore
18142            let data_type = if !self.is_at_end()
18143                && !self.check(TokenType::Comment)
18144                && !self.check(TokenType::Comma)
18145                && !self.check(TokenType::Semicolon)
18146            {
18147                // Check if next token could start a data type
18148                let tok = self.peek();
18149                if tok.token_type.is_keyword()
18150                    || tok.token_type == TokenType::Identifier
18151                    || tok.token_type == TokenType::Var
18152                {
18153                    Some(self.parse_data_type()?)
18154                } else {
18155                    None
18156                }
18157            } else {
18158                None
18159            };
18160            let comment = if self.match_token(TokenType::Comment) {
18161                Some(self.expect_string()?)
18162            } else {
18163                None
18164            };
18165            let cascade = self.match_text_seq(&["CASCADE"]);
18166            // Also check for RESTRICT (the opposite, just consume it)
18167            if !cascade {
18168                self.match_text_seq(&["RESTRICT"]);
18169            }
18170            Ok(AlterTableAction::ChangeColumn {
18171                old_name,
18172                new_name,
18173                data_type,
18174                comment,
18175                cascade,
18176            })
18177        } else if self.match_token(TokenType::Constraint) {
18178            // CONSTRAINT name ... (implicit ADD, CONSTRAINT already consumed)
18179            // Parse the constraint name and then the constraint definition
18180            let name = Some(self.expect_identifier_with_quoted()?);
18181            let constraint = self.parse_constraint_definition(name)?;
18182            Ok(AlterTableAction::AddConstraint(constraint))
18183        } else if self.check(TokenType::PrimaryKey)
18184            || self.check(TokenType::ForeignKey)
18185            || self.check(TokenType::Unique)
18186        {
18187            // ADD CONSTRAINT (implicit ADD, no CONSTRAINT keyword)
18188            let constraint = self.parse_table_constraint()?;
18189            Ok(AlterTableAction::AddConstraint(constraint))
18190        } else if self.match_token(TokenType::Delete) {
18191            // ALTER TABLE t DELETE WHERE x = 1 (BigQuery syntax)
18192            self.expect(TokenType::Where)?;
18193            let where_clause = self.parse_expression()?;
18194            Ok(AlterTableAction::Delete { where_clause })
18195        } else if self.match_keyword("SWAP") {
18196            // Snowflake: ALTER TABLE a SWAP WITH b
18197            self.expect(TokenType::With)?;
18198            let target = self.parse_table_ref()?;
18199            Ok(AlterTableAction::SwapWith(target))
18200        } else if self.match_token(TokenType::Set) {
18201            // TSQL: ALTER TABLE t SET (SYSTEM_VERSIONING=ON, DATA_DELETION=ON, ...)
18202            if self.check(TokenType::LParen) {
18203                self.skip(); // consume (
18204                let mut expressions = Vec::new();
18205                loop {
18206                    if self.check(TokenType::RParen) {
18207                        break;
18208                    }
18209                    if self.check_identifier("SYSTEM_VERSIONING") {
18210                        let expr = self.parse_system_versioning_option()?;
18211                        expressions.push(expr);
18212                    } else if self.check_identifier("DATA_DELETION") {
18213                        let expr = self.parse_data_deletion_option()?;
18214                        expressions.push(expr);
18215                    } else {
18216                        // Generic key=value (e.g., FILESTREAM_ON = 'test')
18217                        let expr = self.parse_expression()?;
18218                        expressions.push(expr);
18219                    }
18220                    if !self.match_token(TokenType::Comma) {
18221                        break;
18222                    }
18223                }
18224                self.expect(TokenType::RParen)?;
18225                Ok(AlterTableAction::SetOptions { expressions })
18226            } else if self.match_keyword("TAG") {
18227                // Snowflake: SET TAG key='value', ... (key can be qualified like schema.tagname)
18228                let mut tags = Vec::new();
18229                loop {
18230                    // Parse qualified tag name (e.g., foo.bar or just bar)
18231                    let mut key = self.expect_identifier_or_keyword()?;
18232                    while self.match_token(TokenType::Dot) {
18233                        let next = self.expect_identifier_or_keyword()?;
18234                        key = format!("{}.{}", key, next);
18235                    }
18236                    self.expect(TokenType::Eq)?;
18237                    let value = self.parse_primary()?;
18238                    tags.push((key, value));
18239                    if !self.match_token(TokenType::Comma) {
18240                        break;
18241                    }
18242                }
18243                Ok(AlterTableAction::SetTag { expressions: tags })
18244            } else if self.check_identifier("LOGGED") {
18245                // PostgreSQL: ALTER TABLE t SET LOGGED
18246                self.skip();
18247                Ok(AlterTableAction::SetAttribute {
18248                    attribute: "LOGGED".to_string(),
18249                })
18250            } else if self.check_identifier("UNLOGGED") {
18251                // PostgreSQL: ALTER TABLE t SET UNLOGGED
18252                self.skip();
18253                Ok(AlterTableAction::SetAttribute {
18254                    attribute: "UNLOGGED".to_string(),
18255                })
18256            } else if self.match_identifier("WITHOUT") {
18257                // PostgreSQL: ALTER TABLE t SET WITHOUT CLUSTER/OIDS
18258                let what = self.expect_identifier_or_keyword()?;
18259                Ok(AlterTableAction::SetAttribute {
18260                    attribute: format!("WITHOUT {}", what),
18261                })
18262            } else if self.check_identifier("ACCESS") {
18263                // PostgreSQL: ALTER TABLE t SET ACCESS METHOD method
18264                self.skip();
18265                // Consume "METHOD"
18266                if !self.match_identifier("METHOD") {
18267                    return Err(self.parse_error("Expected METHOD after ACCESS"));
18268                }
18269                let method = self.expect_identifier_or_keyword()?;
18270                Ok(AlterTableAction::SetAttribute {
18271                    attribute: format!("ACCESS METHOD {}", method),
18272                })
18273            } else if self.check_identifier("TABLESPACE") {
18274                // PostgreSQL: ALTER TABLE t SET TABLESPACE tablespace
18275                self.skip();
18276                let name = self.expect_identifier_or_keyword()?;
18277                Ok(AlterTableAction::SetAttribute {
18278                    attribute: format!("TABLESPACE {}", name),
18279                })
18280            } else if self.check_identifier("STAGE_FILE_FORMAT") {
18281                // Snowflake: ALTER TABLE t SET STAGE_FILE_FORMAT = (options)
18282                self.skip();
18283                let options = self.parse_wrapped_options()?;
18284                Ok(AlterTableAction::SetStageFileFormat { options })
18285            } else if self.check_identifier("STAGE_COPY_OPTIONS") {
18286                // Snowflake: ALTER TABLE t SET STAGE_COPY_OPTIONS = (options)
18287                self.skip();
18288                let options = self.parse_wrapped_options()?;
18289                Ok(AlterTableAction::SetStageCopyOptions { options })
18290            } else if self.match_token(TokenType::Authorization) {
18291                // Trino: ALTER TABLE t SET AUTHORIZATION [ROLE] user
18292                let mut auth_text = String::new();
18293                if self.match_texts(&["ROLE"]) {
18294                    auth_text.push_str("ROLE ");
18295                }
18296                let user = self.expect_identifier_or_keyword()?;
18297                auth_text.push_str(&user);
18298                Ok(AlterTableAction::SetAttribute {
18299                    attribute: format!("AUTHORIZATION {}", auth_text),
18300                })
18301            } else if self.match_identifier("PROPERTIES") {
18302                // Trino: ALTER TABLE t SET PROPERTIES x = 'y', ...
18303                let mut properties = Vec::new();
18304                loop {
18305                    // Parse property name (could be identifier or string literal)
18306                    let key = if self.check(TokenType::String) {
18307                        self.expect_string()?
18308                    } else {
18309                        self.expect_identifier_or_keyword()?
18310                    };
18311                    self.expect(TokenType::Eq)?;
18312                    // Parse value (could be DEFAULT or an expression)
18313                    let value = if self.match_token(TokenType::Default) {
18314                        // Use Var instead of Identifier so it won't be quoted
18315                        Expression::Var(Box::new(crate::expressions::Var {
18316                            this: "DEFAULT".to_string(),
18317                        }))
18318                    } else {
18319                        self.parse_expression()?
18320                    };
18321                    properties.push((key, value));
18322                    if !self.match_token(TokenType::Comma) {
18323                        break;
18324                    }
18325                }
18326                Ok(AlterTableAction::SetProperty { properties })
18327            } else if self.match_text_seq(&["TABLE", "PROPERTIES"]) {
18328                // Redshift: ALTER TABLE t SET TABLE PROPERTIES ('a' = '5', 'b' = 'c')
18329                self.expect(TokenType::LParen)?;
18330                let mut properties = Vec::new();
18331                loop {
18332                    if self.check(TokenType::RParen) {
18333                        break;
18334                    }
18335                    // Parse key (string literal)
18336                    let key = self.parse_primary()?;
18337                    self.expect(TokenType::Eq)?;
18338                    // Parse value (string literal)
18339                    let value = self.parse_primary()?;
18340                    properties.push((key, value));
18341                    if !self.match_token(TokenType::Comma) {
18342                        break;
18343                    }
18344                }
18345                self.expect(TokenType::RParen)?;
18346                Ok(AlterTableAction::SetTableProperties { properties })
18347            } else if self.match_text_seq(&["LOCATION"]) {
18348                // Redshift: ALTER TABLE t SET LOCATION 's3://bucket/folder/'
18349                let location = self.expect_string()?;
18350                Ok(AlterTableAction::SetLocation { location })
18351            } else if self.match_text_seq(&["FILE", "FORMAT"]) {
18352                // Redshift: ALTER TABLE t SET FILE FORMAT AVRO
18353                let format = self.expect_identifier_or_keyword()?;
18354                Ok(AlterTableAction::SetFileFormat { format })
18355            } else {
18356                // Snowflake: SET property=value, ...
18357                let mut properties = Vec::new();
18358                loop {
18359                    let key = self.expect_identifier_or_keyword()?;
18360                    self.expect(TokenType::Eq)?;
18361                    let value = self.parse_expression()?;
18362                    properties.push((key, value));
18363                    if !self.match_token(TokenType::Comma) {
18364                        break;
18365                    }
18366                }
18367                Ok(AlterTableAction::SetProperty { properties })
18368            }
18369        } else if self.match_keyword("UNSET") {
18370            // Snowflake: ALTER TABLE t UNSET property or UNSET TAG key
18371            if self.match_keyword("TAG") {
18372                // UNSET TAG key1, key2 (keys can be qualified like schema.tagname)
18373                let mut names = Vec::new();
18374                loop {
18375                    let mut name = self.expect_identifier_or_keyword()?;
18376                    while self.match_token(TokenType::Dot) {
18377                        let next = self.expect_identifier_or_keyword()?;
18378                        name = format!("{}.{}", name, next);
18379                    }
18380                    names.push(name);
18381                    if !self.match_token(TokenType::Comma) {
18382                        break;
18383                    }
18384                }
18385                Ok(AlterTableAction::UnsetTag { names })
18386            } else {
18387                // UNSET property1, property2
18388                let mut properties = Vec::new();
18389                loop {
18390                    let name = self.expect_identifier_or_keyword()?;
18391                    properties.push(name);
18392                    if !self.match_token(TokenType::Comma) {
18393                        break;
18394                    }
18395                }
18396                Ok(AlterTableAction::UnsetProperty { properties })
18397            }
18398        } else if self.match_keyword("CLUSTER") {
18399            // Snowflake: ALTER TABLE t CLUSTER BY (col1, col2 DESC)
18400            self.expect(TokenType::By)?;
18401            self.expect(TokenType::LParen)?;
18402            // Parse ordered expressions (can have ASC/DESC modifiers)
18403            let ordered = self.parse_order_by_list()?;
18404            // Convert Ordered to Expression (wrapping in Ordered if it has ordering)
18405            let expressions: Vec<Expression> = ordered
18406                .into_iter()
18407                .map(|o| Expression::Ordered(Box::new(o)))
18408                .collect();
18409            self.expect(TokenType::RParen)?;
18410            Ok(AlterTableAction::ClusterBy { expressions })
18411        } else if self.match_token(TokenType::Replace) {
18412            // ClickHouse: REPLACE PARTITION expr FROM table
18413            if self.match_token(TokenType::Partition) {
18414                let partition_expr = if self.match_text_seq(&["ALL"]) {
18415                    Expression::Identifier(Identifier::new("ALL".to_string()))
18416                } else if self.match_text_seq(&["ID"]) {
18417                    let id_val = self.parse_expression()?;
18418                    // Store as Raw to preserve "ID <value>" format
18419                    let id_str = match &id_val {
18420                        Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
18421                            let Literal::String(s) = lit.as_ref() else {
18422                                unreachable!()
18423                            };
18424                            format!("ID '{}'", s)
18425                        }
18426                        _ => format!("ID {}", "?"),
18427                    };
18428                    Expression::Raw(Raw { sql: id_str })
18429                } else {
18430                    self.parse_expression()?
18431                };
18432                let source = if self.match_token(TokenType::From) {
18433                    let tref = self.parse_table_ref()?;
18434                    Some(Box::new(Expression::Table(Box::new(tref))))
18435                } else {
18436                    None
18437                };
18438                Ok(AlterTableAction::ReplacePartition {
18439                    partition: partition_expr,
18440                    source,
18441                })
18442            } else {
18443                Err(self.parse_error("Expected PARTITION after REPLACE in ALTER TABLE"))
18444            }
18445        } else if matches!(
18446            self.config.dialect,
18447            Some(crate::dialects::DialectType::ClickHouse)
18448        ) {
18449            // ClickHouse-specific ALTER TABLE mutations: UPDATE, DELETE, DETACH, ATTACH,
18450            // FREEZE, UNFREEZE, MATERIALIZE, CLEAR, COMMENT COLUMN, MODIFY ORDER BY,
18451            // MOVE PARTITION, FETCH PARTITION, ADD INDEX, DROP INDEX, CLEAR INDEX
18452            // For ClickHouse, consume any unrecognized ALTER TABLE action as Raw
18453            // (covers UPDATE, DELETE, DETACH, ATTACH, FREEZE, MOVE, FETCH, etc.)
18454            {
18455                let keyword = self.advance().text.clone();
18456                let mut tokens: Vec<(String, TokenType)> = vec![(keyword, TokenType::Var)];
18457                let mut paren_depth = 0i32;
18458                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18459                    // Stop at comma only when at top-level (not inside parens) — it separates ALTER actions
18460                    if self.check(TokenType::Comma) && paren_depth == 0 {
18461                        break;
18462                    }
18463                    let token = self.advance();
18464                    if token.token_type == TokenType::LParen {
18465                        paren_depth += 1;
18466                    }
18467                    if token.token_type == TokenType::RParen {
18468                        paren_depth -= 1;
18469                    }
18470                    let text = if token.token_type == TokenType::QuotedIdentifier {
18471                        format!("\"{}\"", token.text)
18472                    } else if token.token_type == TokenType::String {
18473                        format!("'{}'", token.text)
18474                    } else {
18475                        token.text.clone()
18476                    };
18477                    tokens.push((text, token.token_type));
18478                }
18479                Ok(AlterTableAction::Raw {
18480                    sql: self.join_command_tokens(tokens),
18481                })
18482            }
18483        } else if self.check_identifier("REORGANIZE")
18484            || self.check_identifier("COALESCE")
18485            || self.check_identifier("EXCHANGE")
18486            || self.check_identifier("ANALYZE")
18487            || self.check_identifier("OPTIMIZE")
18488            || self.check_identifier("REBUILD")
18489            || self.check_identifier("REPAIR")
18490            || self.check_identifier("DISCARD")
18491            || self.check_identifier("IMPORT")
18492        {
18493            // MySQL partition operations: REORGANIZE PARTITION, COALESCE PARTITION, etc.
18494            // Consume as Raw, respecting parenthesis depth
18495            let keyword = self.advance().text.clone();
18496            let mut tokens: Vec<(String, TokenType)> = vec![(keyword, TokenType::Var)];
18497            let mut paren_depth = 0i32;
18498            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18499                if self.check(TokenType::Comma) && paren_depth == 0 {
18500                    break;
18501                }
18502                let token = self.advance();
18503                if token.token_type == TokenType::LParen {
18504                    paren_depth += 1;
18505                }
18506                if token.token_type == TokenType::RParen {
18507                    paren_depth -= 1;
18508                    if paren_depth < 0 {
18509                        break;
18510                    }
18511                }
18512                let text = if token.token_type == TokenType::QuotedIdentifier {
18513                    format!("\"{}\"", token.text)
18514                } else if token.token_type == TokenType::String {
18515                    format!("'{}'", token.text)
18516                } else {
18517                    token.text.clone()
18518                };
18519                tokens.push((text, token.token_type));
18520            }
18521            Ok(AlterTableAction::Raw {
18522                sql: self.join_command_tokens(tokens),
18523            })
18524        } else {
18525            Err(self.parse_error(format!(
18526                "Expected ADD, DROP, RENAME, ALTER, SET, UNSET, SWAP, CLUSTER, or REPLACE in ALTER TABLE, got {:?}",
18527                self.peek().token_type
18528            )))
18529        }
18530    }
18531
18532    /// Parse TSQL SYSTEM_VERSIONING option in ALTER TABLE SET (...)
18533    /// Handles: SYSTEM_VERSIONING=OFF, SYSTEM_VERSIONING=ON, SYSTEM_VERSIONING=ON(HISTORY_TABLE=..., ...)
18534    fn parse_system_versioning_option(&mut self) -> Result<Expression> {
18535        self.skip(); // consume SYSTEM_VERSIONING
18536        self.expect(TokenType::Eq)?;
18537
18538        let mut prop = WithSystemVersioningProperty {
18539            on: None,
18540            this: None,
18541            data_consistency: None,
18542            retention_period: None,
18543            with_: None,
18544        };
18545
18546        if self.match_identifier("OFF") {
18547            // SYSTEM_VERSIONING=OFF
18548            // on is None => generates OFF
18549            return Ok(Expression::WithSystemVersioningProperty(Box::new(prop)));
18550        }
18551
18552        // SYSTEM_VERSIONING=ON or SYSTEM_VERSIONING=ON(...)
18553        if self.match_token(TokenType::On) || self.match_identifier("ON") {
18554            prop.on = Some(Box::new(Expression::Boolean(BooleanLiteral {
18555                value: true,
18556            })));
18557        }
18558
18559        if self.match_token(TokenType::LParen) {
18560            // Parse options inside ON(...)
18561            loop {
18562                if self.check(TokenType::RParen) {
18563                    break;
18564                }
18565                if self.match_identifier("HISTORY_TABLE") {
18566                    self.expect(TokenType::Eq)?;
18567                    let table = self.parse_table_ref()?;
18568                    prop.this = Some(Box::new(Expression::Table(Box::new(table))));
18569                } else if self.match_identifier("DATA_CONSISTENCY_CHECK") {
18570                    self.expect(TokenType::Eq)?;
18571                    let val = self.expect_identifier_or_keyword()?;
18572                    prop.data_consistency = Some(Box::new(Expression::Identifier(
18573                        Identifier::new(val.to_ascii_uppercase()),
18574                    )));
18575                } else if self.match_identifier("HISTORY_RETENTION_PERIOD") {
18576                    self.expect(TokenType::Eq)?;
18577                    if let Some(rp) = self.parse_retention_period()? {
18578                        prop.retention_period = Some(Box::new(rp));
18579                    }
18580                } else {
18581                    // Skip unknown options
18582                    self.skip();
18583                }
18584                if !self.match_token(TokenType::Comma) {
18585                    break;
18586                }
18587            }
18588            self.expect(TokenType::RParen)?;
18589        }
18590
18591        Ok(Expression::WithSystemVersioningProperty(Box::new(prop)))
18592    }
18593
18594    /// Parse TSQL DATA_DELETION option in ALTER TABLE SET (...)
18595    /// Handles: DATA_DELETION=ON, DATA_DELETION=OFF, DATA_DELETION=ON(FILTER_COLUMN=..., RETENTION_PERIOD=...)
18596    fn parse_data_deletion_option(&mut self) -> Result<Expression> {
18597        self.skip(); // consume DATA_DELETION
18598        self.expect(TokenType::Eq)?;
18599
18600        let on = if self.match_identifier("ON") || self.match_token(TokenType::On) {
18601            true
18602        } else if self.match_identifier("OFF") {
18603            false
18604        } else {
18605            false
18606        };
18607
18608        let on_expr = Box::new(Expression::Boolean(BooleanLiteral { value: on }));
18609        let mut filter_column = None;
18610        let mut retention_period = None;
18611
18612        if self.match_token(TokenType::LParen) {
18613            loop {
18614                if self.check(TokenType::RParen) {
18615                    break;
18616                }
18617                if self.match_identifier("FILTER_COLUMN") {
18618                    self.expect(TokenType::Eq)?;
18619                    let col = self.expect_identifier_or_keyword()?;
18620                    filter_column = Some(Box::new(Expression::boxed_column(Column {
18621                        name: Identifier::new(col),
18622                        table: None,
18623                        join_mark: false,
18624                        trailing_comments: Vec::new(),
18625                        span: None,
18626                        inferred_type: None,
18627                    })));
18628                } else if self.match_identifier("RETENTION_PERIOD") {
18629                    self.expect(TokenType::Eq)?;
18630                    if let Some(rp) = self.parse_retention_period()? {
18631                        retention_period = Some(Box::new(rp));
18632                    }
18633                } else {
18634                    self.skip();
18635                }
18636                if !self.match_token(TokenType::Comma) {
18637                    break;
18638                }
18639            }
18640            self.expect(TokenType::RParen)?;
18641        }
18642
18643        Ok(Expression::DataDeletionProperty(Box::new(
18644            DataDeletionProperty {
18645                on: on_expr,
18646                filter_column,
18647                retention_period,
18648            },
18649        )))
18650    }
18651
18652    /// Parse ALTER COLUMN action
18653    fn parse_alter_column_action(&mut self) -> Result<AlterColumnAction> {
18654        if self.match_token(TokenType::Set) {
18655            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
18656                Ok(AlterColumnAction::SetNotNull)
18657            } else if self.match_token(TokenType::Default) {
18658                let expr = self.parse_primary()?;
18659                Ok(AlterColumnAction::SetDefault(expr))
18660            } else if self.match_identifier("DATA") {
18661                // SET DATA TYPE
18662                // TYPE can be a keyword token or identifier
18663                let _ = self.match_token(TokenType::Type) || self.match_identifier("TYPE");
18664                let data_type = self.parse_data_type()?;
18665                // Optional COLLATE
18666                let collate = if self.match_token(TokenType::Collate) {
18667                    Some(self.expect_identifier_or_keyword()?)
18668                } else {
18669                    None
18670                };
18671                // Optional USING expression
18672                let using = if self.match_token(TokenType::Using) {
18673                    Some(self.parse_expression()?)
18674                } else {
18675                    None
18676                };
18677                Ok(AlterColumnAction::SetDataType {
18678                    data_type,
18679                    using,
18680                    collate,
18681                })
18682            } else if self.match_identifier("VISIBLE") {
18683                Ok(AlterColumnAction::SetVisible)
18684            } else if self.match_identifier("INVISIBLE") {
18685                Ok(AlterColumnAction::SetInvisible)
18686            } else {
18687                Err(self.parse_error("Expected NOT NULL, DEFAULT, VISIBLE, or INVISIBLE after SET"))
18688            }
18689        } else if self.match_token(TokenType::Drop) {
18690            if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
18691                Ok(AlterColumnAction::DropNotNull)
18692            } else if self.match_token(TokenType::Default) {
18693                Ok(AlterColumnAction::DropDefault)
18694            } else {
18695                Err(self.parse_error("Expected NOT NULL or DEFAULT after DROP"))
18696            }
18697        } else if self.match_token(TokenType::Comment) {
18698            // ALTER COLUMN col COMMENT 'comment'
18699            let comment = self.expect_string()?;
18700            Ok(AlterColumnAction::Comment(comment))
18701        } else if self.match_token(TokenType::Type)
18702            || self.match_identifier("TYPE")
18703            || self.is_identifier_token()
18704        {
18705            // TYPE data_type or just data_type (PostgreSQL/Redshift: ALTER COLUMN col TYPE datatype)
18706            let data_type = self.parse_data_type()?;
18707            // Optional COLLATE
18708            let collate = if self.match_token(TokenType::Collate) {
18709                Some(self.expect_identifier_or_keyword()?)
18710            } else {
18711                None
18712            };
18713            // Optional USING expression
18714            let using = if self.match_token(TokenType::Using) {
18715                Some(self.parse_expression()?)
18716            } else {
18717                None
18718            };
18719            Ok(AlterColumnAction::SetDataType {
18720                data_type,
18721                using,
18722                collate,
18723            })
18724        } else {
18725            Err(self.parse_error("Expected SET, DROP, or TYPE in ALTER COLUMN"))
18726        }
18727    }
18728
18729    /// Parse TRUNCATE statement
18730    fn parse_truncate(&mut self) -> Result<Expression> {
18731        self.expect(TokenType::Truncate)?;
18732
18733        // ClickHouse: TRUNCATE ALL TABLES FROM [IF EXISTS] db
18734        if matches!(
18735            self.config.dialect,
18736            Some(crate::dialects::DialectType::ClickHouse)
18737        ) && self.check_identifier("ALL")
18738            && self.current + 1 < self.tokens.len()
18739            && self.tokens[self.current + 1]
18740                .text
18741                .eq_ignore_ascii_case("TABLES")
18742        {
18743            // Consume remaining tokens as Command
18744            let mut parts = vec!["TRUNCATE".to_string()];
18745            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
18746                let token = self.advance();
18747                if token.token_type == TokenType::String {
18748                    parts.push(format!("'{}'", token.text));
18749                } else {
18750                    parts.push(token.text.clone());
18751                }
18752            }
18753            return Ok(Expression::Command(Box::new(crate::expressions::Command {
18754                this: parts.join(" "),
18755            })));
18756        }
18757
18758        let target = if self.match_token(TokenType::Database) {
18759            TruncateTarget::Database
18760        } else {
18761            // ClickHouse: TRUNCATE TEMPORARY TABLE t
18762            self.match_token(TokenType::Temporary);
18763            self.match_token(TokenType::Table); // optional TABLE keyword
18764            TruncateTarget::Table
18765        };
18766
18767        // Parse optional IF EXISTS
18768        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
18769
18770        // Parse first table with optional ONLY modifier
18771        let has_only = self.match_token(TokenType::Only);
18772        let mut table = self.parse_table_ref()?;
18773        if has_only {
18774            table.only = true;
18775        }
18776
18777        // Check for * suffix on table name (PostgreSQL: inherit children)
18778        let first_star = self.match_token(TokenType::Star);
18779
18780        // TSQL: WITH (PARTITIONS(1, 2 TO 5, 10 TO 20, 84))
18781        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
18782            if let Some(hint_expr) = self.parse_truncate_table_hints()? {
18783                match hint_expr {
18784                    Expression::Tuple(tuple) => {
18785                        table.hints = tuple.expressions;
18786                    }
18787                    other => {
18788                        table.hints = vec![other];
18789                    }
18790                }
18791            }
18792        }
18793
18794        // ClickHouse: ON CLUSTER clause
18795        let on_cluster = self.parse_on_cluster_clause()?;
18796
18797        // Parse additional tables for multi-table TRUNCATE
18798        let mut extra_tables = Vec::new();
18799        if first_star {
18800            // The first table has a * suffix, so create an entry for it
18801            extra_tables.push(TruncateTableEntry {
18802                table: table.clone(),
18803                star: true,
18804            });
18805        }
18806        while self.match_token(TokenType::Comma) {
18807            let extra_only = self.match_token(TokenType::Only);
18808            let mut extra_table = self.parse_table_ref()?;
18809            if extra_only {
18810                extra_table.only = true;
18811            }
18812            let extra_star = self.match_token(TokenType::Star);
18813            extra_tables.push(TruncateTableEntry {
18814                table: extra_table,
18815                star: extra_star,
18816            });
18817        }
18818
18819        // Parse RESTART IDENTITY / CONTINUE IDENTITY
18820        // RESTART is TokenType::Restart keyword, IDENTITY is TokenType::Identity keyword
18821        let identity = if self.match_token(TokenType::Restart) {
18822            self.match_token(TokenType::Identity);
18823            Some(TruncateIdentity::Restart)
18824        } else if self.match_identifier("CONTINUE") {
18825            self.match_token(TokenType::Identity);
18826            Some(TruncateIdentity::Continue)
18827        } else {
18828            None
18829        };
18830
18831        // Parse CASCADE or RESTRICT
18832        // CASCADE is TokenType::Cascade keyword, RESTRICT is TokenType::Restrict keyword
18833        let cascade = self.match_token(TokenType::Cascade);
18834        let restrict = if !cascade {
18835            self.match_token(TokenType::Restrict)
18836        } else {
18837            false
18838        };
18839
18840        // Parse Hive PARTITION clause: PARTITION(key = value, ...)
18841        // parse_partition consumes the PARTITION keyword itself
18842        let partition = self.parse_partition()?;
18843
18844        // ClickHouse: TRUNCATE TABLE t SETTINGS key=value, ...
18845        if matches!(
18846            self.config.dialect,
18847            Some(crate::dialects::DialectType::ClickHouse)
18848        ) && self.match_token(TokenType::Settings)
18849        {
18850            // Consume settings expressions (they're not stored in the AST for TRUNCATE)
18851            loop {
18852                let _ = self.parse_expression()?;
18853                if !self.match_token(TokenType::Comma) {
18854                    break;
18855                }
18856            }
18857        }
18858
18859        Ok(Expression::Truncate(Box::new(Truncate {
18860            target,
18861            if_exists,
18862            table,
18863            on_cluster,
18864            cascade,
18865            extra_tables,
18866            identity,
18867            restrict,
18868            partition: partition.map(Box::new),
18869        })))
18870    }
18871
18872    /// Parse VALUES table constructor: VALUES (1, 'a'), (2, 'b')
18873    fn parse_values(&mut self) -> Result<Expression> {
18874        self.expect(TokenType::Values)?;
18875
18876        let mut expressions = Vec::new();
18877
18878        // Handle bare VALUES without parentheses: VALUES 1, 2, 3 -> VALUES (1), (2), (3)
18879        if !self.check(TokenType::LParen) {
18880            loop {
18881                let val = self.parse_expression()?;
18882                expressions.push(Tuple {
18883                    expressions: vec![val],
18884                });
18885                if !self.match_token(TokenType::Comma) {
18886                    break;
18887                }
18888            }
18889        } else {
18890            loop {
18891                self.expect(TokenType::LParen)?;
18892                // Parse VALUES tuple elements with optional AS aliases (Hive syntax)
18893                let row_values = self.parse_values_expression_list()?;
18894                self.expect(TokenType::RParen)?;
18895
18896                expressions.push(Tuple {
18897                    expressions: row_values,
18898                });
18899
18900                if !self.match_token(TokenType::Comma) {
18901                    break;
18902                }
18903                // ClickHouse: allow trailing comma after last tuple
18904                if matches!(
18905                    self.config.dialect,
18906                    Some(crate::dialects::DialectType::ClickHouse)
18907                ) && !self.check(TokenType::LParen)
18908                {
18909                    break;
18910                }
18911            }
18912        }
18913
18914        // Check for alias: VALUES (1, 2) AS new_data or VALUES (1, 2) new_data
18915        let (alias, column_aliases) = if self.match_token(TokenType::As) {
18916            let alias_name = self.expect_identifier()?;
18917            let alias = Some(Identifier::new(alias_name));
18918
18919            // Check for column aliases: AS new_data(a, b)
18920            let col_aliases = if self.match_token(TokenType::LParen) {
18921                let aliases = self.parse_identifier_list()?;
18922                self.expect(TokenType::RParen)?;
18923                aliases
18924            } else {
18925                Vec::new()
18926            };
18927            (alias, col_aliases)
18928        } else if self.check(TokenType::Var) && !self.check_keyword() {
18929            // Implicit alias: VALUES (0) foo(bar)
18930            let alias_name = self.advance().text.clone();
18931            let alias = Some(Identifier::new(alias_name));
18932            let col_aliases = if self.match_token(TokenType::LParen) {
18933                let aliases = self.parse_identifier_list()?;
18934                self.expect(TokenType::RParen)?;
18935                aliases
18936            } else {
18937                Vec::new()
18938            };
18939            (alias, col_aliases)
18940        } else {
18941            (None, Vec::new())
18942        };
18943
18944        // VALUES can be followed by set operations (UNION, etc.)
18945        let values_expr = Expression::Values(Box::new(Values {
18946            expressions,
18947            alias,
18948            column_aliases,
18949        }));
18950
18951        // Check for set operations after VALUES
18952        self.parse_set_operation(values_expr)
18953    }
18954
18955    /// Parse USE statement: USE db, USE DATABASE x, USE SCHEMA x.y, USE ROLE x, etc.
18956    fn parse_use(&mut self) -> Result<Expression> {
18957        self.expect(TokenType::Use)?;
18958
18959        // Check for Snowflake: USE SECONDARY ROLES ALL|NONE|role1, role2, ...
18960        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("SECONDARY") {
18961            self.skip(); // consume SECONDARY
18962                         // Check for ROLES
18963            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLES") {
18964                self.skip(); // consume ROLES
18965                             // Parse ALL, NONE, or comma-separated role list
18966                let mut roles = Vec::new();
18967                loop {
18968                    if self.check(TokenType::Var)
18969                        || self.check(TokenType::All)
18970                        || self.check(TokenType::Identifier)
18971                    {
18972                        let role = self.advance().text.clone();
18973                        roles.push(role);
18974                        if !self.match_token(TokenType::Comma) {
18975                            break;
18976                        }
18977                    } else {
18978                        break;
18979                    }
18980                }
18981                let name = if roles.is_empty() {
18982                    "ALL".to_string()
18983                } else {
18984                    roles.join(", ")
18985                };
18986                return Ok(Expression::Use(Box::new(Use {
18987                    kind: Some(UseKind::SecondaryRoles),
18988                    this: Identifier::new(name),
18989                })));
18990            }
18991        }
18992
18993        // Check for kind: DATABASE, SCHEMA, ROLE, WAREHOUSE, CATALOG
18994        // Note: ROLE and CATALOG are not keywords, so we check the text
18995        let kind = if self.match_token(TokenType::Database) {
18996            Some(UseKind::Database)
18997        } else if self.match_token(TokenType::Schema) {
18998            Some(UseKind::Schema)
18999        } else if self.match_token(TokenType::Warehouse) {
19000            Some(UseKind::Warehouse)
19001        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLE") {
19002            self.skip();
19003            Some(UseKind::Role)
19004        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("CATALOG") {
19005            self.skip();
19006            Some(UseKind::Catalog)
19007        } else {
19008            None
19009        };
19010
19011        // Parse the name (can be qualified like x.y)
19012        // Use expect_identifier_or_keyword_with_quoted because names like "default", "system" are valid
19013        let mut ident = self.expect_identifier_or_keyword_with_quoted()?;
19014
19015        // Handle qualified names like schema.table for USE SCHEMA x.y
19016        if self.match_token(TokenType::Dot) {
19017            let second_part = self.expect_identifier_or_keyword_with_quoted()?;
19018            ident.name = format!("{}.{}", ident.name, second_part.name);
19019        }
19020
19021        Ok(Expression::Use(Box::new(Use { kind, this: ident })))
19022    }
19023
19024    /// Parse EXPORT DATA statement (BigQuery)
19025    /// EXPORT DATA [WITH CONNECTION connection] OPTIONS (...) AS SELECT ...
19026    fn parse_export_data(&mut self) -> Result<Expression> {
19027        self.skip(); // consume EXPORT
19028
19029        // Expect DATA
19030        if !self.match_identifier("DATA") {
19031            return Err(self.parse_error("Expected DATA after EXPORT"));
19032        }
19033
19034        // Optional: WITH CONNECTION connection
19035        let connection = if self.match_text_seq(&["WITH", "CONNECTION"]) {
19036            // Parse connection identifier (can be qualified: project.location.connection)
19037            let first = self.expect_identifier()?;
19038            let connection_name = if self.match_token(TokenType::Dot) {
19039                let second = self.expect_identifier()?;
19040                if self.match_token(TokenType::Dot) {
19041                    let third = self.expect_identifier()?;
19042                    format!("{}.{}.{}", first, second, third)
19043                } else {
19044                    format!("{}.{}", first, second)
19045                }
19046            } else {
19047                first
19048            };
19049            Some(Box::new(Expression::Identifier(Identifier::new(
19050                connection_name,
19051            ))))
19052        } else {
19053            None
19054        };
19055
19056        // Expect OPTIONS (...)
19057        let options = if self.match_identifier("OPTIONS") {
19058            self.parse_options_list()?
19059        } else {
19060            Vec::new()
19061        };
19062
19063        // Expect AS
19064        self.expect(TokenType::As)?;
19065
19066        // Parse the SELECT query
19067        let query = self.parse_statement()?;
19068
19069        Ok(Expression::Export(Box::new(Export {
19070            this: Box::new(query),
19071            connection,
19072            options,
19073        })))
19074    }
19075
19076    /// Parse CACHE TABLE statement (Spark)
19077    /// CACHE [LAZY] TABLE name [OPTIONS(...)] [AS query]
19078    fn parse_cache(&mut self) -> Result<Expression> {
19079        self.expect(TokenType::Cache)?;
19080
19081        // Check for LAZY keyword
19082        let lazy = self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("LAZY");
19083        if lazy {
19084            self.skip();
19085        }
19086
19087        self.expect(TokenType::Table)?;
19088        let table = Identifier::new(self.expect_identifier()?);
19089
19090        // Check for OPTIONS clause
19091        let options =
19092            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTIONS") {
19093                self.skip();
19094                self.expect(TokenType::LParen)?;
19095                let mut opts = Vec::new();
19096                loop {
19097                    // Parse key = value pairs (key can be string literal or identifier)
19098                    let key = if self.check(TokenType::NationalString) {
19099                        let token = self.advance();
19100                        Expression::Literal(Box::new(Literal::NationalString(token.text)))
19101                    } else if self.check(TokenType::String) {
19102                        let token = self.advance();
19103                        Expression::Literal(Box::new(Literal::String(token.text)))
19104                    } else {
19105                        Expression::Identifier(Identifier::new(self.expect_identifier()?))
19106                    };
19107                    // Eq is optional - Spark allows space-separated key value pairs
19108                    // e.g., OPTIONS ('storageLevel' 'DISK_ONLY') or OPTIONS ('key' = 'value')
19109                    let _ = self.match_token(TokenType::Eq);
19110                    let value = self.parse_expression()?;
19111                    opts.push((key, value));
19112                    if !self.match_token(TokenType::Comma) {
19113                        break;
19114                    }
19115                }
19116                self.expect(TokenType::RParen)?;
19117                opts
19118            } else {
19119                Vec::new()
19120            };
19121
19122        // Check for AS clause or implicit query (SELECT without AS in Spark)
19123        let query = if self.match_token(TokenType::As) {
19124            Some(self.parse_statement()?)
19125        } else if self.check(TokenType::Select) || self.check(TokenType::With) {
19126            // Spark allows SELECT without AS keyword after CACHE TABLE
19127            Some(self.parse_statement()?)
19128        } else {
19129            None
19130        };
19131
19132        Ok(Expression::Cache(Box::new(Cache {
19133            table,
19134            lazy,
19135            options,
19136            query,
19137        })))
19138    }
19139
19140    /// Parse UNCACHE TABLE statement (Spark)
19141    /// UNCACHE TABLE [IF EXISTS] name
19142    fn parse_uncache(&mut self) -> Result<Expression> {
19143        self.expect(TokenType::Uncache)?;
19144        self.expect(TokenType::Table)?;
19145
19146        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
19147        let table = Identifier::new(self.expect_identifier()?);
19148
19149        Ok(Expression::Uncache(Box::new(Uncache { table, if_exists })))
19150    }
19151
19152    /// Parse LOAD DATA statement (Hive)
19153    /// LOAD DATA [LOCAL] INPATH 'path' [OVERWRITE] INTO TABLE table_name
19154    /// [PARTITION (col=val, ...)] [INPUTFORMAT 'format'] [SERDE 'serde']
19155    fn parse_load_data(&mut self) -> Result<Expression> {
19156        self.expect(TokenType::Load)?;
19157
19158        // Expect DATA keyword
19159        let data_token = self.advance();
19160        if !data_token.text.eq_ignore_ascii_case("DATA") {
19161            return Err(self.parse_error("Expected DATA after LOAD"));
19162        }
19163
19164        // Check for LOCAL keyword
19165        let local = self.match_token(TokenType::Local);
19166
19167        // Expect INPATH
19168        self.expect(TokenType::Inpath)?;
19169
19170        // Parse the path (string literal)
19171        let inpath = if self.check(TokenType::String) {
19172            self.advance().text
19173        } else {
19174            return Err(self.parse_error("Expected string literal after INPATH"));
19175        };
19176
19177        // Check for OVERWRITE keyword
19178        let overwrite = self.match_token(TokenType::Overwrite);
19179
19180        // Expect INTO TABLE
19181        self.expect(TokenType::Into)?;
19182        self.expect(TokenType::Table)?;
19183
19184        // Parse table name (can be qualified)
19185        let table = Expression::Table(Box::new(self.parse_table_ref()?));
19186
19187        // Check for PARTITION clause
19188        let partition = if self.match_token(TokenType::Partition) {
19189            self.expect(TokenType::LParen)?;
19190            let mut partitions = Vec::new();
19191            loop {
19192                let col = Identifier::new(self.expect_identifier_or_keyword()?);
19193                self.expect(TokenType::Eq)?;
19194                let val = self.parse_expression()?;
19195                partitions.push((col, val));
19196                if !self.match_token(TokenType::Comma) {
19197                    break;
19198                }
19199            }
19200            self.expect(TokenType::RParen)?;
19201            partitions
19202        } else {
19203            Vec::new()
19204        };
19205
19206        // Check for INPUTFORMAT clause
19207        let input_format = if self.match_token(TokenType::InputFormat) {
19208            if self.check(TokenType::String) {
19209                Some(self.advance().text)
19210            } else {
19211                return Err(self.parse_error("Expected string literal after INPUTFORMAT"));
19212            }
19213        } else {
19214            None
19215        };
19216
19217        // Check for SERDE clause
19218        let serde = if self.match_token(TokenType::Serde) {
19219            if self.check(TokenType::String) {
19220                Some(self.advance().text)
19221            } else {
19222                return Err(self.parse_error("Expected string literal after SERDE"));
19223            }
19224        } else {
19225            None
19226        };
19227
19228        Ok(Expression::LoadData(Box::new(LoadData {
19229            local,
19230            inpath,
19231            overwrite,
19232            table,
19233            partition,
19234            input_format,
19235            serde,
19236        })))
19237    }
19238
19239    /// Parse PRAGMA statement (SQLite)
19240    /// PRAGMA [schema.]name [= value | (args...)]
19241    fn parse_pragma(&mut self) -> Result<Expression> {
19242        self.expect(TokenType::Pragma)?;
19243
19244        // Parse schema.name or just name
19245        let first_name = self.expect_identifier_or_keyword()?;
19246
19247        let (schema, name) = if self.match_token(TokenType::Dot) {
19248            // First name was schema
19249            let pragma_name = self.expect_identifier_or_keyword()?;
19250            (
19251                Some(Identifier::new(first_name)),
19252                Identifier::new(pragma_name),
19253            )
19254        } else {
19255            (None, Identifier::new(first_name))
19256        };
19257
19258        // Check for assignment or function call
19259        let (value, args) = if self.match_token(TokenType::Eq) {
19260            // PRAGMA name = value
19261            let val = self.parse_expression()?;
19262            (Some(val), Vec::new())
19263        } else if self.match_token(TokenType::LParen) {
19264            // PRAGMA name(args...)
19265            let mut arguments = Vec::new();
19266            if !self.check(TokenType::RParen) {
19267                loop {
19268                    arguments.push(self.parse_expression()?);
19269                    if !self.match_token(TokenType::Comma) {
19270                        break;
19271                    }
19272                }
19273            }
19274            self.expect(TokenType::RParen)?;
19275            (None, arguments)
19276        } else {
19277            (None, Vec::new())
19278        };
19279
19280        Ok(Expression::Pragma(Box::new(Pragma {
19281            schema,
19282            name,
19283            value,
19284            args,
19285        })))
19286    }
19287
19288    /// Parse ROLLBACK statement
19289    /// ROLLBACK [TO [SAVEPOINT] <name>]
19290    fn parse_rollback(&mut self) -> Result<Expression> {
19291        self.expect(TokenType::Rollback)?;
19292
19293        // Check for optional TRANSACTION, TRAN, or WORK keyword
19294        let has_transaction = self.match_token(TokenType::Transaction)
19295            || self.match_identifier("TRAN")
19296            || self.match_identifier("WORK");
19297
19298        // Check for TO SAVEPOINT (standard SQL) or transaction name (TSQL)
19299        let (savepoint, this) = if self.match_token(TokenType::To) {
19300            // Optional SAVEPOINT keyword
19301            self.match_token(TokenType::Savepoint);
19302            // Savepoint name
19303            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
19304                let name = self.advance().text;
19305                (
19306                    Some(Box::new(Expression::Identifier(Identifier::new(name)))),
19307                    None,
19308                )
19309            } else {
19310                (None, None)
19311            }
19312        } else if has_transaction
19313            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19314        {
19315            // TSQL: ROLLBACK TRANSACTION transaction_name
19316            let name = self.advance().text;
19317            (
19318                None,
19319                Some(Box::new(Expression::Identifier(Identifier::new(name)))),
19320            )
19321        } else if has_transaction {
19322            // Just ROLLBACK TRANSACTION - store marker
19323            (
19324                None,
19325                Some(Box::new(Expression::Identifier(Identifier::new(
19326                    "TRANSACTION".to_string(),
19327                )))),
19328            )
19329        } else {
19330            (None, None)
19331        };
19332
19333        Ok(Expression::Rollback(Box::new(Rollback { savepoint, this })))
19334    }
19335
19336    /// Parse COMMIT statement
19337    /// COMMIT [TRANSACTION|TRAN|WORK] [transaction_name] [WITH (DELAYED_DURABILITY = ON|OFF)] [AND [NO] CHAIN]
19338    fn parse_commit(&mut self) -> Result<Expression> {
19339        self.expect(TokenType::Commit)?;
19340
19341        // Check for optional TRANSACTION, TRAN, or WORK keyword
19342        let has_transaction = self.match_token(TokenType::Transaction)
19343            || self.match_identifier("TRAN")
19344            || self.match_identifier("WORK");
19345
19346        // Parse optional transaction name (TSQL)
19347        let this = if has_transaction
19348            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19349            && !self.check(TokenType::With)
19350            && !self.check(TokenType::And)
19351        {
19352            let name = self.advance().text;
19353            Some(Box::new(Expression::Identifier(Identifier::new(name))))
19354        } else if has_transaction {
19355            // Store marker that TRANSACTION keyword was present
19356            Some(Box::new(Expression::Identifier(Identifier::new(
19357                "TRANSACTION".to_string(),
19358            ))))
19359        } else {
19360            None
19361        };
19362
19363        // Parse WITH (DELAYED_DURABILITY = ON|OFF) for TSQL
19364        let durability = if self.match_token(TokenType::With) && self.match_token(TokenType::LParen)
19365        {
19366            // Check for DELAYED_DURABILITY
19367            if self.match_identifier("DELAYED_DURABILITY") && self.match_token(TokenType::Eq) {
19368                // ON is a keyword (TokenType::On), OFF is an identifier
19369                let on = self.match_token(TokenType::On) || self.match_identifier("ON");
19370                if !on {
19371                    self.match_identifier("OFF");
19372                }
19373                self.expect(TokenType::RParen)?;
19374                Some(Box::new(Expression::Boolean(BooleanLiteral { value: on })))
19375            } else {
19376                // Skip to RParen
19377                while !self.check(TokenType::RParen) && !self.is_at_end() {
19378                    self.skip();
19379                }
19380                self.match_token(TokenType::RParen);
19381                None
19382            }
19383        } else {
19384            None
19385        };
19386
19387        // Parse AND [NO] CHAIN
19388        let chain = if self.match_token(TokenType::And) {
19389            let no_chain = self.match_token(TokenType::No);
19390            self.match_identifier("CHAIN");
19391            if no_chain {
19392                // AND NO CHAIN - explicit false
19393                Some(Box::new(Expression::Boolean(BooleanLiteral {
19394                    value: false,
19395                })))
19396            } else {
19397                // AND CHAIN - explicit true
19398                Some(Box::new(Expression::Boolean(BooleanLiteral {
19399                    value: true,
19400                })))
19401            }
19402        } else {
19403            None
19404        };
19405
19406        Ok(Expression::Commit(Box::new(Commit {
19407            chain,
19408            this,
19409            durability,
19410        })))
19411    }
19412
19413    /// Parse END statement (PostgreSQL alias for COMMIT)
19414    /// END [WORK|TRANSACTION] [AND [NO] CHAIN]
19415    fn parse_end_transaction(&mut self) -> Result<Expression> {
19416        self.expect(TokenType::End)?;
19417
19418        // Check for optional WORK or TRANSACTION keyword
19419        let _has_work = self.match_identifier("WORK") || self.match_token(TokenType::Transaction);
19420
19421        // Parse AND [NO] CHAIN
19422        let chain = if self.match_token(TokenType::And) {
19423            let no_chain = self.match_token(TokenType::No);
19424            self.match_identifier("CHAIN");
19425            if no_chain {
19426                // AND NO CHAIN - explicit false
19427                Some(Box::new(Expression::Boolean(BooleanLiteral {
19428                    value: false,
19429                })))
19430            } else {
19431                // AND CHAIN - explicit true
19432                Some(Box::new(Expression::Boolean(BooleanLiteral {
19433                    value: true,
19434                })))
19435            }
19436        } else {
19437            None
19438        };
19439
19440        // Return as COMMIT since END is an alias
19441        Ok(Expression::Commit(Box::new(Commit {
19442            chain,
19443            this: None,
19444            durability: None,
19445        })))
19446    }
19447
19448    /// Parse BEGIN/START TRANSACTION statement
19449    /// BEGIN [DEFERRED|IMMEDIATE|EXCLUSIVE] [TRANSACTION|TRAN|WORK] [transaction_name] [WITH MARK 'description']
19450    /// Also handles procedural BEGIN blocks (BigQuery, etc.): BEGIN statement_list END
19451    fn parse_transaction(&mut self) -> Result<Expression> {
19452        self.expect(TokenType::Begin)?;
19453
19454        // Check if this is a procedural BEGIN block rather than a transaction
19455        // If next token is not a transaction keyword and we have more tokens, it's a procedural block
19456        let is_transaction = self.is_at_end()
19457            || self.check(TokenType::Semicolon)
19458            || self.check(TokenType::Transaction)
19459            || self.check_identifier("TRAN")
19460            || self.check_identifier("WORK")
19461            || self.check_identifier("DEFERRED")
19462            || self.check_identifier("IMMEDIATE")
19463            || self.check_identifier("EXCLUSIVE");
19464
19465        if !is_transaction {
19466            // TSQL: BEGIN TRY ... END TRY [BEGIN CATCH ... END CATCH]
19467            // These are block-structured constructs that may contain semicolons,
19468            // so we can't use parse_command() which stops at the first semicolon.
19469            let is_try = self.check_identifier("TRY");
19470            let is_catch = self.check_identifier("CATCH");
19471            if is_try || is_catch {
19472                let block_kind = if is_try { "TRY" } else { "CATCH" };
19473                self.skip(); // consume TRY or CATCH
19474                let mut tokens: Vec<(String, TokenType)> = vec![
19475                    ("BEGIN".to_string(), TokenType::Begin),
19476                    (block_kind.to_string(), TokenType::Var),
19477                ];
19478                // Collect tokens until matching END TRY / END CATCH
19479                while !self.is_at_end() {
19480                    if self.check(TokenType::End)
19481                        && self.current + 1 < self.tokens.len()
19482                        && self.tokens[self.current + 1]
19483                            .text
19484                            .eq_ignore_ascii_case(block_kind)
19485                    {
19486                        tokens.push(("END".to_string(), TokenType::End));
19487                        self.skip(); // consume END
19488                        tokens.push((block_kind.to_string(), TokenType::Var));
19489                        self.skip(); // consume TRY/CATCH
19490                        break;
19491                    }
19492                    let token = self.advance();
19493                    let text = if token.token_type == TokenType::String {
19494                        format!("'{}'", token.text)
19495                    } else if token.token_type == TokenType::QuotedIdentifier {
19496                        format!("\"{}\"", token.text)
19497                    } else {
19498                        token.text.clone()
19499                    };
19500                    tokens.push((text, token.token_type));
19501                }
19502                let mut result = Expression::Command(Box::new(Command {
19503                    this: self.join_command_tokens(tokens),
19504                }));
19505
19506                // If this was a TRY block, check for a following BEGIN CATCH block
19507                if is_try
19508                    && self.check(TokenType::Begin)
19509                    && self.current + 1 < self.tokens.len()
19510                    && self.tokens[self.current + 1]
19511                        .text
19512                        .eq_ignore_ascii_case("CATCH")
19513                {
19514                    // Recursively parse the BEGIN CATCH block
19515                    let catch_block = self.parse_transaction()?;
19516                    // Combine TRY and CATCH into a single command
19517                    if let (Expression::Command(try_cmd), Expression::Command(catch_cmd)) =
19518                        (&result, &catch_block)
19519                    {
19520                        result = Expression::Command(Box::new(Command {
19521                            this: format!("{} {}", try_cmd.this, catch_cmd.this),
19522                        }));
19523                    }
19524                }
19525
19526                return Ok(result);
19527            }
19528
19529            // This is a procedural BEGIN block - parse as Command
19530            // Collect remaining tokens until end of statement
19531            return self
19532                .parse_command()?
19533                .ok_or_else(|| self.parse_error("Failed to parse BEGIN block"));
19534        }
19535
19536        // Check for transaction kind: DEFERRED, IMMEDIATE, EXCLUSIVE (SQLite)
19537        let kind = if self.match_identifier("DEFERRED")
19538            || self.match_identifier("IMMEDIATE")
19539            || self.match_identifier("EXCLUSIVE")
19540        {
19541            Some(self.previous().text.clone())
19542        } else {
19543            None
19544        };
19545
19546        // Check for TRANSACTION, TRAN, or WORK keyword
19547        let has_transaction_keyword = self.match_token(TokenType::Transaction)
19548            || self.match_identifier("TRAN")
19549            || self.match_identifier("WORK");
19550
19551        // Parse optional transaction name (TSQL style: BEGIN TRANSACTION trans_name)
19552        let trans_name = if has_transaction_keyword
19553            && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19554            && !self.check(TokenType::With)
19555        {
19556            // Could be a transaction name or @variable
19557            let name = self.advance().text;
19558            Some(name)
19559        } else {
19560            None
19561        };
19562
19563        // Combine kind and trans_name into `this`
19564        let this = if let Some(name) = trans_name {
19565            Some(Box::new(Expression::Identifier(Identifier::new(name))))
19566        } else if let Some(k) = kind {
19567            Some(Box::new(Expression::Identifier(Identifier::new(k))))
19568        } else {
19569            None
19570        };
19571
19572        // Parse WITH MARK 'description' (TSQL)
19573        let mark = if self.match_token(TokenType::With) && self.match_identifier("MARK") {
19574            if self.check(TokenType::String) {
19575                let desc = self.advance().text;
19576                Some(Box::new(Expression::Literal(Box::new(Literal::String(
19577                    desc,
19578                )))))
19579            } else {
19580                Some(Box::new(Expression::Literal(Box::new(Literal::String(
19581                    "".to_string(),
19582                )))))
19583            }
19584        } else if has_transaction_keyword {
19585            // Store "TRANSACTION" marker to preserve round-trip
19586            Some(Box::new(Expression::Identifier(Identifier::new(
19587                "TRANSACTION".to_string(),
19588            ))))
19589        } else {
19590            None
19591        };
19592
19593        // Parse any additional transaction modes (isolation levels, etc.)
19594        let mut mode_parts: Vec<String> = Vec::new();
19595        while self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
19596            let mut mode_tokens: Vec<String> = Vec::new();
19597            while (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19598                && !self.check(TokenType::Comma)
19599            {
19600                mode_tokens.push(self.advance().text);
19601            }
19602            if !mode_tokens.is_empty() {
19603                mode_parts.push(mode_tokens.join(" "));
19604            }
19605            if !self.match_token(TokenType::Comma) {
19606                break;
19607            }
19608        }
19609
19610        let modes = if !mode_parts.is_empty() {
19611            Some(Box::new(Expression::Identifier(Identifier::new(
19612                mode_parts.join(", "),
19613            ))))
19614        } else {
19615            None
19616        };
19617
19618        Ok(Expression::Transaction(Box::new(Transaction {
19619            this,
19620            modes,
19621            mark,
19622        })))
19623    }
19624
19625    /// Parse START TRANSACTION statement
19626    /// START TRANSACTION [READ ONLY | READ WRITE] [, ISOLATION LEVEL ...]
19627    fn parse_start_transaction(&mut self) -> Result<Expression> {
19628        self.expect(TokenType::Start)?;
19629
19630        // Expect TRANSACTION keyword
19631        self.expect(TokenType::Transaction)?;
19632
19633        // Parse any transaction modes (READ ONLY, READ WRITE, ISOLATION LEVEL, etc.)
19634        let mut mode_parts: Vec<String> = Vec::new();
19635        while self.is_identifier_token()
19636            || self.is_safe_keyword_as_identifier()
19637            || self.match_identifier("READ")
19638        {
19639            // If we matched READ, add it to tokens
19640            let read_matched = if self.previous().text.eq_ignore_ascii_case("READ") {
19641                true
19642            } else {
19643                false
19644            };
19645            let mut mode_tokens: Vec<String> = Vec::new();
19646            if read_matched {
19647                mode_tokens.push("READ".to_string());
19648            }
19649            while (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19650                && !self.check(TokenType::Comma)
19651            {
19652                mode_tokens.push(self.advance().text);
19653            }
19654            if !mode_tokens.is_empty() {
19655                mode_parts.push(mode_tokens.join(" "));
19656            }
19657            if !self.match_token(TokenType::Comma) {
19658                break;
19659            }
19660        }
19661
19662        let modes = if !mode_parts.is_empty() {
19663            Some(Box::new(Expression::Identifier(Identifier::new(
19664                mode_parts.join(", "),
19665            ))))
19666        } else {
19667            None
19668        };
19669
19670        Ok(Expression::Transaction(Box::new(Transaction {
19671            this: None, // START TRANSACTION doesn't have a kind like DEFERRED/IMMEDIATE
19672            modes,
19673            // Mark as START to differentiate from BEGIN
19674            mark: Some(Box::new(Expression::Identifier(Identifier::new(
19675                "START".to_string(),
19676            )))),
19677        })))
19678    }
19679
19680    /// Parse DESCRIBE statement
19681    /// DESCRIBE [EXTENDED|FORMATTED|ANALYZE] <table_or_query>
19682    /// Also handles EXPLAIN (parsed as Describe)
19683    fn parse_describe(&mut self) -> Result<Expression> {
19684        // Accept DESCRIBE, DESC, and EXPLAIN (Var token)
19685        // Capture leading comments from the first token
19686        let leading_comments = if self.check(TokenType::Describe) {
19687            let token = self.advance();
19688            token.comments
19689        } else if self.check(TokenType::Desc) {
19690            let token = self.advance();
19691            token.comments
19692        } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("EXPLAIN") {
19693            let token = self.advance(); // consume EXPLAIN
19694            token.comments
19695        } else {
19696            return Err(self.parse_error("Expected DESCRIBE, DESC, or EXPLAIN"));
19697        };
19698
19699        // Check for EXTENDED or FORMATTED keywords
19700        let extended = self.match_identifier("EXTENDED");
19701        let formatted = if !extended {
19702            self.match_identifier("FORMATTED")
19703        } else {
19704            false
19705        };
19706
19707        // Check for style keywords like ANALYZE, HISTORY
19708        // ClickHouse: EXPLAIN SYNTAX/AST/PLAN/PIPELINE/ESTIMATE/TABLE OVERRIDE/CURRENT TRANSACTION
19709        // For HISTORY, we need to look ahead to ensure it's not part of a schema-qualified
19710        // table name like "history.tbl". If the next token is a Dot, "history" is a schema name.
19711        let style = if !extended && !formatted && self.match_identifier("ANALYZE") {
19712            Some("ANALYZE".to_string())
19713        } else if !extended
19714            && !formatted
19715            && matches!(
19716                self.config.dialect,
19717                Some(crate::dialects::DialectType::ClickHouse)
19718            )
19719        {
19720            // ClickHouse EXPLAIN styles
19721            let text_upper = if !self.is_at_end() {
19722                self.peek().text.to_ascii_uppercase()
19723            } else {
19724                String::new()
19725            };
19726            match text_upper.as_str() {
19727                "SYNTAX" | "AST" | "PLAN" | "PIPELINE" | "ESTIMATE" | "QUERY" | "CURRENT" => {
19728                    self.skip();
19729                    let mut style_str = text_upper;
19730                    // Handle multi-word: TABLE OVERRIDE, CURRENT TRANSACTION, QUERY TREE
19731                    if style_str == "CURRENT" && self.check_identifier("TRANSACTION") {
19732                        style_str.push_str(" TRANSACTION");
19733                        self.skip();
19734                    }
19735                    if style_str == "QUERY" && self.check_identifier("TREE") {
19736                        style_str.push_str(" TREE");
19737                        self.skip();
19738                    }
19739                    Some(style_str)
19740                }
19741                _ if self.check(TokenType::Table) => {
19742                    // EXPLAIN TABLE OVERRIDE
19743                    self.skip(); // consume TABLE
19744                    if self.check_identifier("OVERRIDE") {
19745                        self.skip();
19746                        Some("TABLE OVERRIDE".to_string())
19747                    } else {
19748                        // Not TABLE OVERRIDE, backtrack
19749                        self.current -= 1;
19750                        None
19751                    }
19752                }
19753                _ => None,
19754            }
19755        } else if !extended
19756            && !formatted
19757            && (self.check(TokenType::Identifier)
19758                || self.check(TokenType::Var)
19759                || self.check(TokenType::QuotedIdentifier))
19760            && self.peek().text.eq_ignore_ascii_case("HISTORY")
19761            && self.peek_nth(1).map(|t| t.token_type) != Some(TokenType::Dot)
19762        {
19763            self.skip(); // consume HISTORY
19764            Some("HISTORY".to_string())
19765        } else {
19766            None
19767        };
19768
19769        // Check for object kind like SEMANTIC VIEW, TABLE, INPUT, OUTPUT, etc.
19770        let kind = if self.match_identifier("SEMANTIC") {
19771            if self.match_token(TokenType::View) {
19772                Some("SEMANTIC VIEW".to_string())
19773            } else {
19774                Some("SEMANTIC".to_string())
19775            }
19776        } else if self.match_token(TokenType::Table) {
19777            Some("TABLE".to_string())
19778        } else if self.match_token(TokenType::View) {
19779            Some("VIEW".to_string())
19780        } else if self.match_identifier("DATABASE") {
19781            Some("DATABASE".to_string())
19782        } else if self.match_identifier("SCHEMA") {
19783            Some("SCHEMA".to_string())
19784        } else if self.match_token(TokenType::Input) {
19785            Some("INPUT".to_string())
19786        } else if self.match_token(TokenType::Output) {
19787            Some("OUTPUT".to_string())
19788        } else {
19789            None
19790        };
19791
19792        // ClickHouse: parse EXPLAIN settings before the target statement
19793        // e.g., EXPLAIN actions=1, description=0 SELECT ...
19794        // e.g., EXPLAIN PLAN actions=1 SELECT ...
19795        let mut properties = Vec::new();
19796        if matches!(
19797            self.config.dialect,
19798            Some(crate::dialects::DialectType::ClickHouse)
19799        ) {
19800            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
19801                // Look for key=value pairs before a statement keyword
19802                if (self.is_identifier_token()
19803                    || self.is_safe_keyword_as_identifier()
19804                    || self.check(TokenType::Type))
19805                    && self.current + 1 < self.tokens.len()
19806                    && self.tokens[self.current + 1].token_type == TokenType::Eq
19807                {
19808                    let name = self.advance().text.to_lowercase();
19809                    self.skip(); // consume =
19810                    let value = self.advance().text.clone();
19811                    properties.push((name, value));
19812                    self.match_token(TokenType::Comma); // optional comma between settings
19813                } else {
19814                    break;
19815                }
19816            }
19817        }
19818
19819        // Parse target - could be a table name or a SELECT/INSERT/other statement
19820        // ClickHouse: EXPLAIN/DESC can precede any statement or subquery
19821        let target = if self.check(TokenType::Select) || self.check(TokenType::With) {
19822            self.parse_statement()?
19823        } else if self.check(TokenType::LParen) && {
19824            // Look through nested parens for SELECT/WITH
19825            let mut depth = 0usize;
19826            let mut found_select = false;
19827            for i in 0..100 {
19828                match self.peek_nth(i).map(|t| t.token_type) {
19829                    Some(TokenType::LParen) => depth += 1,
19830                    Some(TokenType::Select) | Some(TokenType::With) if depth > 0 => {
19831                        found_select = true;
19832                        break;
19833                    }
19834                    _ => break,
19835                }
19836            }
19837            found_select
19838        } {
19839            // DESC (((SELECT ...))) — deeply nested parenthesized subquery
19840            self.parse_statement()?
19841        } else if matches!(
19842            self.config.dialect,
19843            Some(crate::dialects::DialectType::ClickHouse)
19844        ) && (self.check(TokenType::Insert)
19845            || self.check(TokenType::Create)
19846            || self.check(TokenType::Alter)
19847            || self.check(TokenType::Drop)
19848            || self.check(TokenType::Set)
19849            || self.check(TokenType::System))
19850        {
19851            self.parse_statement()?
19852        } else if matches!(
19853            self.config.dialect,
19854            Some(crate::dialects::DialectType::ClickHouse)
19855        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
19856            && self.peek_nth(1).map(|t| t.token_type) == Some(TokenType::LParen)
19857        {
19858            // ClickHouse: DESC format(Values, '(123)') — function call as target
19859            self.parse_expression()?
19860        } else {
19861            // Parse as table reference
19862            let table = self.parse_table_ref()?;
19863            Expression::Table(Box::new(table))
19864        };
19865
19866        // Parse optional PARTITION clause (Spark/Hive)
19867        let partition = if self.match_token(TokenType::Partition) {
19868            // PARTITION(key = value, ...)
19869            self.expect(TokenType::LParen)?;
19870            // Parse partition expressions (e.g., ds = '2024-01-01')
19871            let mut partition_exprs = Vec::new();
19872            loop {
19873                if let Some(expr) = self.parse_conjunction()? {
19874                    partition_exprs.push(expr);
19875                }
19876                if !self.match_token(TokenType::Comma) {
19877                    break;
19878                }
19879            }
19880            self.expect(TokenType::RParen)?;
19881            let partition = Expression::Partition(Box::new(crate::expressions::Partition {
19882                expressions: partition_exprs,
19883                subpartition: false,
19884            }));
19885            Some(Box::new(partition))
19886        } else {
19887            None
19888        };
19889
19890        // ClickHouse: consume optional SETTINGS clause after target
19891        // e.g., DESC format(CSV, '...') SETTINGS key='val', key2='val2'
19892        if matches!(
19893            self.config.dialect,
19894            Some(crate::dialects::DialectType::ClickHouse)
19895        ) && self.check(TokenType::Settings)
19896        {
19897            self.skip(); // consume SETTINGS
19898            let _ = self.parse_settings_property()?;
19899        }
19900
19901        // Databricks: DESCRIBE ... AS JSON
19902        let as_json = if self.check(TokenType::As)
19903            && self
19904                .peek_nth(1)
19905                .map(|t| t.text.eq_ignore_ascii_case("JSON"))
19906                == Some(true)
19907        {
19908            self.skip(); // consume AS
19909            self.skip(); // consume JSON
19910            true
19911        } else {
19912            false
19913        };
19914
19915        // Parse optional post-target properties like type=stage (non-ClickHouse)
19916        if properties.is_empty() {
19917            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
19918                // Check for identifier or keyword that could be a property name
19919                if self.check(TokenType::Var) || self.check(TokenType::Type) || self.check_keyword()
19920                {
19921                    let name = self.advance().text.to_lowercase();
19922                    if self.match_token(TokenType::Eq) {
19923                        let value = self.advance().text.clone();
19924                        properties.push((name, value));
19925                    } else {
19926                        // Not a property, put it back (can't easily undo, so break)
19927                        break;
19928                    }
19929                } else {
19930                    break;
19931                }
19932            }
19933        }
19934
19935        Ok(Expression::Describe(Box::new(Describe {
19936            target,
19937            extended,
19938            formatted,
19939            kind,
19940            properties,
19941            style,
19942            partition,
19943            leading_comments,
19944            as_json,
19945        })))
19946    }
19947
19948    /// Parse SHOW statement
19949    /// SHOW [TERSE] <object_type> [HISTORY] [LIKE pattern] [IN <scope>] [STARTS WITH pattern] [LIMIT n] [FROM object]
19950    fn parse_show(&mut self) -> Result<Expression> {
19951        self.expect(TokenType::Show)?;
19952
19953        // Check for TERSE
19954        let terse = self.match_identifier("TERSE");
19955
19956        // Parse the thing to show (DATABASES, TABLES, SCHEMAS, etc.)
19957        // This can be multiple words like "PRIMARY KEYS" or "IMPORTED KEYS"
19958        let mut this_parts = Vec::new();
19959        let mut target: Option<Expression> = None;
19960        let mut mutex: Option<bool> = None;
19961
19962        // Consume identifier tokens until we hit a keyword like LIKE, IN, FROM, LIMIT, HISTORY
19963        // Special handling for SingleStore SHOW variations
19964        while !self.is_at_end() {
19965            let current = self.peek();
19966            // Stop at keywords that start clauses
19967            if matches!(
19968                current.token_type,
19969                TokenType::Like
19970                    | TokenType::In
19971                    | TokenType::From
19972                    | TokenType::Limit
19973                    | TokenType::Semicolon
19974                    | TokenType::Eof
19975                    | TokenType::Where
19976                    | TokenType::For
19977                    | TokenType::Offset
19978                    | TokenType::Settings
19979            ) {
19980                // ClickHouse: SHOW CREATE SETTINGS PROFILE - don't stop at SETTINGS
19981                if current.token_type == TokenType::Settings
19982                    && matches!(
19983                        self.config.dialect,
19984                        Some(crate::dialects::DialectType::ClickHouse)
19985                    )
19986                    && this_parts.join(" ") == "CREATE"
19987                {
19988                    // Fall through to process SETTINGS as part of the type name
19989                } else {
19990                    break;
19991                }
19992            }
19993            // Handle comma-separated profile types (e.g., SHOW PROFILE BLOCK IO, PAGE FAULTS)
19994            // Append comma to the last part to preserve spacing
19995            if current.token_type == TokenType::Comma {
19996                if !this_parts.is_empty() {
19997                    let last = this_parts.pop().unwrap();
19998                    this_parts.push(format!("{},", last));
19999                }
20000                self.skip();
20001                continue;
20002            }
20003            // Stop at HISTORY keyword (but not as the first word)
20004            if !this_parts.is_empty() && current.text.eq_ignore_ascii_case("HISTORY") {
20005                break;
20006            }
20007            // Stop at STARTS keyword
20008            if current.text.eq_ignore_ascii_case("STARTS") {
20009                break;
20010            }
20011            // SingleStore: SHOW PLAN <id> - handle number directly (before Var/keyword check)
20012            // This is needed because numbers don't pass the Var/keyword check
20013            let joined_check = this_parts.join(" ");
20014            if joined_check == "PLAN" && current.token_type == TokenType::Number {
20015                let id = self.advance().text;
20016                target = Some(Expression::Literal(Box::new(Literal::Number(id))));
20017                break;
20018            }
20019            // Accept identifiers and keywords as part of the object type
20020            if current.token_type == TokenType::Var || current.token_type.is_keyword() {
20021                let joined = this_parts.join(" ");
20022
20023                // SingleStore: SHOW CREATE <type> <name> - preserve case for name
20024                // Types: AGGREGATE, PIPELINE, PROJECTION
20025                if matches!(
20026                    joined.as_str(),
20027                    "CREATE AGGREGATE" | "CREATE PIPELINE" | "CREATE PROJECTION"
20028                ) {
20029                    let name = self.advance().text;
20030                    target = Some(Expression::Identifier(Identifier::new(name)));
20031                    break;
20032                }
20033
20034                // SingleStore: SHOW <type> ON <name> - preserve case for name after ON
20035                // Check if current token is "ON" (but not at start)
20036                if current.text.eq_ignore_ascii_case("ON") && !this_parts.is_empty() {
20037                    this_parts.push("ON".to_string());
20038                    self.skip();
20039                    // Parse the name after ON, preserving case
20040                    if !self.is_at_end() {
20041                        let next = self.peek();
20042                        // Handle "ON TABLE name" pattern
20043                        if next.text.eq_ignore_ascii_case("TABLE") {
20044                            this_parts.push("TABLE".to_string());
20045                            self.skip();
20046                        }
20047                        // Parse the actual name
20048                        if !self.is_at_end() {
20049                            let name_tok = self.peek();
20050                            if name_tok.token_type == TokenType::Var
20051                                || name_tok.token_type.is_keyword()
20052                            {
20053                                let name = self.advance().text;
20054                                target = Some(Expression::Identifier(Identifier::new(name)));
20055                            }
20056                        }
20057                    }
20058                    break;
20059                }
20060
20061                // SingleStore: SHOW REPRODUCTION INTO OUTFILE 'filename'
20062                if current.text.eq_ignore_ascii_case("INTO") && joined == "REPRODUCTION" {
20063                    this_parts.push("INTO".to_string());
20064                    self.skip();
20065                    if !self.is_at_end() && self.peek().text.eq_ignore_ascii_case("OUTFILE") {
20066                        this_parts.push("OUTFILE".to_string());
20067                        self.skip();
20068                        // Parse the filename
20069                        if !self.is_at_end() && self.check(TokenType::String) {
20070                            let filename = self.advance().text;
20071                            target = Some(Expression::Literal(Box::new(Literal::String(filename))));
20072                        }
20073                    }
20074                    break;
20075                }
20076
20077                // SingleStore: SHOW PLAN [JSON] <id> - capture the numeric ID
20078                if joined == "PLAN" {
20079                    // Check if current is "JSON" - if so, push it and check for number
20080                    if current.text.eq_ignore_ascii_case("JSON") {
20081                        this_parts.push("JSON".to_string());
20082                        self.skip();
20083                        // Now check for number
20084                        if !self.is_at_end() && self.check(TokenType::Number) {
20085                            let id = self.advance().text;
20086                            target = Some(Expression::Literal(Box::new(Literal::Number(id))));
20087                        }
20088                        break;
20089                    }
20090                    // Check if current is a number (plan ID)
20091                    if current.token_type == TokenType::Number {
20092                        let id = self.advance().text;
20093                        target = Some(Expression::Literal(Box::new(Literal::Number(id))));
20094                        break;
20095                    }
20096                }
20097
20098                this_parts.push(current.text.to_ascii_uppercase());
20099                self.skip();
20100
20101                // ClickHouse: SHOW CREATE TABLE/VIEW/DICTIONARY <qualified_name>
20102                // After detecting CREATE TABLE/VIEW/DICTIONARY, parse the next as a table ref
20103                let joined = this_parts.join(" ");
20104                if matches!(
20105                    joined.as_str(),
20106                    "CREATE TABLE"
20107                        | "CREATE VIEW"
20108                        | "CREATE DICTIONARY"
20109                        | "CREATE DATABASE"
20110                        | "CREATE MATERIALIZED VIEW"
20111                        | "CREATE LIVE VIEW"
20112                ) {
20113                    if !self.is_at_end()
20114                        && (self.check(TokenType::Var)
20115                            || self.check(TokenType::QuotedIdentifier)
20116                            || self.is_safe_keyword_as_identifier())
20117                    {
20118                        let table = self.parse_table_ref()?;
20119                        target = Some(Expression::Table(Box::new(table)));
20120                    }
20121                    break;
20122                }
20123
20124                // ClickHouse: SHOW CREATE ROLE/PROFILE/QUOTA/ROW POLICY/POLICY with multi-name or ON clause
20125                // These have complex syntax (comma-separated names, ON db.table) - consume as raw text
20126                if matches!(
20127                    self.config.dialect,
20128                    Some(crate::dialects::DialectType::ClickHouse)
20129                ) && (matches!(
20130                    joined.as_str(),
20131                    "CREATE ROLE"
20132                        | "CREATE QUOTA"
20133                        | "CREATE SETTINGS PROFILE"
20134                        | "CREATE PROFILE"
20135                        | "CREATE ROW POLICY"
20136                        | "CREATE POLICY"
20137                        | "CREATE USER"
20138                ) || matches!(
20139                    joined.as_str(),
20140                    "SHOW CREATE ROLE"
20141                        | "SHOW CREATE QUOTA"
20142                        | "SHOW CREATE SETTINGS PROFILE"
20143                        | "SHOW CREATE PROFILE"
20144                        | "SHOW CREATE ROW POLICY"
20145                        | "SHOW CREATE POLICY"
20146                        | "SHOW CREATE USER"
20147                )) {
20148                    let mut parts = Vec::new();
20149                    while !self.is_at_end() && self.peek().token_type != TokenType::Semicolon {
20150                        parts.push(self.advance().text.clone());
20151                    }
20152                    target = Some(Expression::Identifier(Identifier::new(parts.join(" "))));
20153                    break;
20154                }
20155
20156                // ClickHouse: SHOW CREATE <qualified_name> (without TABLE/VIEW keyword)
20157                // e.g., SHOW CREATE INFORMATION_SCHEMA.COLUMNS
20158                if joined == "CREATE"
20159                    && matches!(
20160                        self.config.dialect,
20161                        Some(crate::dialects::DialectType::ClickHouse)
20162                    )
20163                    && !self.is_at_end()
20164                    && (self.check(TokenType::Var) || self.check(TokenType::QuotedIdentifier))
20165                    && !matches!(
20166                        self.peek().text.to_ascii_uppercase().as_str(),
20167                        "TABLE"
20168                            | "VIEW"
20169                            | "DICTIONARY"
20170                            | "DATABASE"
20171                            | "MATERIALIZED"
20172                            | "LIVE"
20173                            | "TEMPORARY"
20174                            | "ROLE"
20175                            | "QUOTA"
20176                            | "POLICY"
20177                            | "PROFILE"
20178                            | "USER"
20179                            | "ROW"
20180                            | "SETTINGS"
20181                    )
20182                {
20183                    let table = self.parse_table_ref()?;
20184                    target = Some(Expression::Table(Box::new(table)));
20185                    break;
20186                }
20187
20188                // Special handling for ENGINE: the next token is the engine name (case-preserved)
20189                // followed by STATUS or MUTEX
20190                if joined == "ENGINE" {
20191                    // Parse engine name (case-preserved)
20192                    if !self.is_at_end() {
20193                        let engine_tok = self.peek();
20194                        if engine_tok.token_type == TokenType::Var
20195                            || engine_tok.token_type.is_keyword()
20196                        {
20197                            let engine_name = self.advance().text;
20198                            target = Some(Expression::Identifier(Identifier::new(engine_name)));
20199                            // Parse STATUS or MUTEX
20200                            if !self.is_at_end() {
20201                                let next = self.peek();
20202                                let next_upper = next.text.to_ascii_uppercase();
20203                                if next_upper == "STATUS" {
20204                                    self.skip();
20205                                    mutex = Some(false);
20206                                } else if next_upper == "MUTEX" {
20207                                    self.skip();
20208                                    mutex = Some(true);
20209                                }
20210                            }
20211                        }
20212                    }
20213                    break;
20214                }
20215            } else {
20216                break;
20217            }
20218        }
20219
20220        let this = this_parts.join(" ");
20221
20222        // Check for HISTORY
20223        let history = self.match_identifier("HISTORY");
20224
20225        // Check for FOR target (MySQL: SHOW GRANTS FOR foo, SHOW PROFILE ... FOR QUERY 5)
20226        // SingleStore: SHOW GROUPS FOR ROLE 'role_name', SHOW GROUPS FOR USER 'username'
20227        let for_target = if self.match_token(TokenType::For) {
20228            // Parse the target (can be multi-word like QUERY 5, or ROLE 'name')
20229            let mut parts = Vec::new();
20230            while !self.is_at_end() {
20231                let tok = self.peek();
20232                if matches!(
20233                    tok.token_type,
20234                    TokenType::Like
20235                        | TokenType::In
20236                        | TokenType::From
20237                        | TokenType::Limit
20238                        | TokenType::Semicolon
20239                        | TokenType::Eof
20240                        | TokenType::Where
20241                ) {
20242                    break;
20243                }
20244                if tok.token_type == TokenType::Var
20245                    || tok.token_type.is_keyword()
20246                    || tok.token_type == TokenType::Number
20247                {
20248                    parts.push(self.advance().text);
20249                } else if tok.token_type == TokenType::String {
20250                    // Handle string literals (e.g., SHOW GROUPS FOR ROLE 'role_name')
20251                    let text = self.advance().text;
20252                    parts.push(format!("'{}'", text));
20253                } else {
20254                    break;
20255                }
20256            }
20257            if parts.is_empty() {
20258                None
20259            } else {
20260                Some(Expression::Identifier(Identifier::new(parts.join(" "))))
20261            }
20262        } else {
20263            None
20264        };
20265
20266        // Check for LIKE pattern
20267        let like = if self.match_token(TokenType::Like) {
20268            Some(self.parse_primary()?)
20269        } else {
20270            None
20271        };
20272
20273        // Check for IN scope
20274        let (scope_kind, scope) = if self.match_token(TokenType::In) {
20275            // Parse scope kind and optionally scope object
20276            // Check for keywords: ACCOUNT, DATABASE, SCHEMA, TABLE, CLASS, APPLICATION
20277            let (kind, scope_obj) = if self.match_keyword("ACCOUNT") {
20278                (Some("ACCOUNT".to_string()), None)
20279            } else if self.match_token(TokenType::Database) {
20280                // IN DATABASE [name]
20281                let scope_obj = if !self.is_at_end()
20282                    && !self.check(TokenType::Like)
20283                    && !self.check(TokenType::Limit)
20284                    && !self.check(TokenType::Semicolon)
20285                    && !self.check_keyword_text("STARTS")
20286                {
20287                    let table = self.parse_table_ref()?;
20288                    Some(Expression::Table(Box::new(table)))
20289                } else {
20290                    None
20291                };
20292                (Some("DATABASE".to_string()), scope_obj)
20293            } else if self.match_token(TokenType::Schema) {
20294                // IN SCHEMA [name]
20295                let scope_obj = if !self.is_at_end()
20296                    && !self.check(TokenType::Like)
20297                    && !self.check(TokenType::Limit)
20298                    && !self.check(TokenType::Semicolon)
20299                    && !self.check_keyword_text("STARTS")
20300                {
20301                    let table = self.parse_table_ref()?;
20302                    Some(Expression::Table(Box::new(table)))
20303                } else {
20304                    None
20305                };
20306                (Some("SCHEMA".to_string()), scope_obj)
20307            } else if self.match_token(TokenType::Table) {
20308                // IN TABLE [name]
20309                let scope_obj = if !self.is_at_end()
20310                    && !self.check(TokenType::Like)
20311                    && !self.check(TokenType::Limit)
20312                    && !self.check(TokenType::Semicolon)
20313                    && !self.check_keyword_text("STARTS")
20314                {
20315                    let table = self.parse_table_ref()?;
20316                    Some(Expression::Table(Box::new(table)))
20317                } else {
20318                    None
20319                };
20320                (Some("TABLE".to_string()), scope_obj)
20321            } else if self.match_token(TokenType::View) {
20322                // IN VIEW [name]
20323                let scope_obj = if !self.is_at_end()
20324                    && !self.check(TokenType::Like)
20325                    && !self.check(TokenType::Limit)
20326                    && !self.check(TokenType::Semicolon)
20327                    && !self.check_keyword_text("STARTS")
20328                {
20329                    let table = self.parse_table_ref()?;
20330                    Some(Expression::Table(Box::new(table)))
20331                } else {
20332                    None
20333                };
20334                (Some("VIEW".to_string()), scope_obj)
20335            } else if self.match_keyword("CLASS") {
20336                // IN CLASS name
20337                let scope_obj = if !self.is_at_end() {
20338                    let table = self.parse_table_ref()?;
20339                    Some(Expression::Table(Box::new(table)))
20340                } else {
20341                    None
20342                };
20343                (Some("CLASS".to_string()), scope_obj)
20344            } else if self.match_keyword("APPLICATION") {
20345                // IN APPLICATION [PACKAGE] name
20346                let kind = if self.match_keyword("PACKAGE") {
20347                    "APPLICATION PACKAGE".to_string()
20348                } else {
20349                    "APPLICATION".to_string()
20350                };
20351                let scope_obj = if !self.is_at_end() {
20352                    let table = self.parse_table_ref()?;
20353                    Some(Expression::Table(Box::new(table)))
20354                } else {
20355                    None
20356                };
20357                (Some(kind), scope_obj)
20358            } else {
20359                // Default - infer scope_kind based on what we're showing
20360                // Python SQLGlot: SCHEMA_KINDS = {"OBJECTS", "TABLES", "VIEWS", "SEQUENCES", "UNIQUE KEYS", "IMPORTED KEYS"}
20361                let table = self.parse_table_ref()?;
20362                let inferred_kind = match this.as_str() {
20363                    "OBJECTS" | "TABLES" | "VIEWS" | "SEQUENCES" | "UNIQUE KEYS"
20364                    | "IMPORTED KEYS" => "SCHEMA",
20365                    "PRIMARY KEYS" => "TABLE",
20366                    _ => "SCHEMA", // Default to SCHEMA for unknown types
20367                };
20368                (
20369                    Some(inferred_kind.to_string()),
20370                    Some(Expression::Table(Box::new(table))),
20371                )
20372            };
20373            (kind, scope_obj)
20374        } else {
20375            (None, None)
20376        };
20377
20378        // Check for STARTS WITH
20379        let starts_with = if self.match_keyword("STARTS") {
20380            self.match_token(TokenType::With); // WITH is a keyword token
20381            Some(self.parse_primary()?)
20382        } else {
20383            None
20384        };
20385
20386        // Check for LIMIT
20387        let limit = if self.match_token(TokenType::Limit) {
20388            Some(Box::new(Limit {
20389                this: self.parse_expression()?,
20390                percent: false,
20391                comments: Vec::new(),
20392            }))
20393        } else {
20394            None
20395        };
20396
20397        // Check for FROM (can be a string literal or identifier)
20398        // For MySQL SHOW COLUMNS/INDEX, the first FROM is the target table,
20399        // and the second FROM is the database
20400        let mut from = if self.match_token(TokenType::From) {
20401            Some(self.parse_primary()?)
20402        } else {
20403            None
20404        };
20405
20406        // Check for second FROM clause (MySQL: SHOW COLUMNS FROM tbl FROM db, SHOW INDEX FROM foo FROM bar)
20407        let mut db = if from.is_some() && self.match_token(TokenType::From) {
20408            Some(self.parse_primary()?)
20409        } else {
20410            None
20411        };
20412
20413        // Normalize MySQL SHOW INDEX/COLUMNS FROM db.tbl -> FROM tbl FROM db.
20414        if matches!(this.as_str(), "INDEX" | "COLUMNS") && db.is_none() {
20415            if let Some(from_expr) = from.take() {
20416                match from_expr {
20417                    Expression::Table(mut t) => {
20418                        if let Some(db_ident) = t.schema.take().or(t.catalog.take()) {
20419                            db = Some(Expression::Identifier(db_ident));
20420                            from = Some(Expression::Identifier(t.name));
20421                        } else {
20422                            from = Some(Expression::Table(t));
20423                        }
20424                    }
20425                    Expression::Column(c) => {
20426                        if let Some(table_ident) = c.table {
20427                            db = Some(Expression::Identifier(table_ident));
20428                            from = Some(Expression::Identifier(c.name));
20429                        } else {
20430                            from = Some(Expression::Column(c));
20431                        }
20432                    }
20433                    Expression::Identifier(id) => {
20434                        if let Some((db_name, table_name)) = id.name.split_once('.') {
20435                            db = Some(Expression::Identifier(Identifier::new(db_name)));
20436                            from = Some(Expression::Identifier(Identifier {
20437                                name: table_name.to_string(),
20438                                quoted: id.quoted,
20439                                trailing_comments: id.trailing_comments,
20440                                span: None,
20441                            }));
20442                        } else {
20443                            from = Some(Expression::Identifier(id));
20444                        }
20445                    }
20446                    other => {
20447                        from = Some(other);
20448                    }
20449                }
20450            }
20451        }
20452
20453        // MySQL: SHOW TABLES FROM db LIKE 'pattern' (LIKE can come after FROM)
20454        let like = if like.is_none() && self.match_token(TokenType::Like) {
20455            Some(self.parse_primary()?)
20456        } else {
20457            like
20458        };
20459
20460        // ClickHouse: SHOW ... NOT LIKE 'pattern' / NOT ILIKE 'pattern'
20461        if matches!(
20462            self.config.dialect,
20463            Some(crate::dialects::DialectType::ClickHouse)
20464        ) && self.check(TokenType::Not)
20465        {
20466            if self.current + 1 < self.tokens.len()
20467                && matches!(
20468                    self.tokens[self.current + 1].token_type,
20469                    TokenType::Like | TokenType::ILike
20470                )
20471            {
20472                self.skip(); // consume NOT
20473                self.skip(); // consume LIKE/ILIKE
20474                let _ = self.parse_primary()?; // consume pattern
20475            }
20476        }
20477
20478        // ClickHouse: SHOW ... ILIKE 'pattern'
20479        if matches!(
20480            self.config.dialect,
20481            Some(crate::dialects::DialectType::ClickHouse)
20482        ) && self.match_token(TokenType::ILike)
20483        {
20484            let _ = self.parse_primary()?; // consume pattern
20485        }
20486
20487        // Check for WHERE clause (MySQL: SHOW STATUS WHERE condition)
20488        let where_clause = if self.match_token(TokenType::Where) {
20489            Some(self.parse_expression()?)
20490        } else {
20491            None
20492        };
20493
20494        // Check for WITH PRIVILEGES clause (Snowflake: SHOW ... WITH PRIVILEGES USAGE, MODIFY)
20495        let privileges = if self.match_token(TokenType::With) && self.match_keyword("PRIVILEGES") {
20496            // Parse comma-separated list of privilege names (no parentheses)
20497            let mut privs = Vec::new();
20498            loop {
20499                if self.is_at_end() || self.check(TokenType::Semicolon) {
20500                    break;
20501                }
20502                let tok = self.peek();
20503                if tok.token_type == TokenType::Var || tok.token_type.is_keyword() {
20504                    privs.push(self.advance().text.to_ascii_uppercase());
20505                    // Check for comma to continue
20506                    if !self.match_token(TokenType::Comma) {
20507                        break;
20508                    }
20509                } else {
20510                    break;
20511                }
20512            }
20513            privs
20514        } else {
20515            Vec::new()
20516        };
20517
20518        // ClickHouse: SHOW ... SETTINGS key=val, key=val
20519        if matches!(
20520            self.config.dialect,
20521            Some(crate::dialects::DialectType::ClickHouse)
20522        ) {
20523            self.parse_clickhouse_settings_clause()?;
20524        }
20525
20526        Ok(Expression::Show(Box::new(Show {
20527            this,
20528            terse,
20529            history,
20530            like,
20531            scope_kind,
20532            scope,
20533            starts_with,
20534            limit,
20535            from,
20536            where_clause,
20537            for_target,
20538            db,
20539            target,
20540            mutex,
20541            privileges,
20542        })))
20543    }
20544
20545    /// Parse COPY statement (Snowflake, PostgreSQL)
20546    /// COPY INTO <table> FROM <source> [(<parameters>)]
20547    /// COPY INTO <location> FROM <table> [(<parameters>)]
20548    fn parse_copy(&mut self) -> Result<Expression> {
20549        self.expect(TokenType::Copy)?;
20550
20551        // Check for INTO (Snowflake/TSQL style: COPY INTO)
20552        let is_into = self.match_token(TokenType::Into);
20553
20554        // Parse target table or location (possibly with column list)
20555        let this = if self.check(TokenType::LParen) {
20556            // Subquery: COPY (SELECT ...) TO ...
20557            self.parse_primary()?
20558        } else if self.check(TokenType::DAt)
20559            || self.check(TokenType::String)
20560            || self.is_stage_reference()
20561        {
20562            // Stage or file destination (for exports): COPY INTO @stage or COPY INTO 's3://...'
20563            self.parse_file_location()?
20564        } else {
20565            // Table reference, possibly with column list: COPY table (col1, col2)
20566            let table = self.parse_table_ref()?;
20567            // Check for column list
20568            if self.check(TokenType::LParen) {
20569                // Peek ahead to see if this is a column list or a subquery
20570                // Column list won't start with SELECT
20571                let has_column_list = {
20572                    let start = self.current;
20573                    self.skip(); // consume (
20574                    let is_select = self.check(TokenType::Select);
20575                    self.current = start; // backtrack
20576                    !is_select
20577                };
20578                if has_column_list {
20579                    self.skip(); // consume (
20580                    let mut columns = Vec::new();
20581                    loop {
20582                        let col_name = self.expect_identifier_or_keyword()?;
20583                        columns.push(col_name);
20584                        if !self.match_token(TokenType::Comma) {
20585                            break;
20586                        }
20587                    }
20588                    self.expect(TokenType::RParen)?;
20589                    // Create a schema expression with the table and columns
20590                    Expression::Schema(Box::new(Schema {
20591                        this: Some(Box::new(Expression::Table(Box::new(table)))),
20592                        expressions: columns
20593                            .into_iter()
20594                            .map(|c| {
20595                                Expression::boxed_column(Column {
20596                                    name: Identifier::new(c),
20597                                    table: None,
20598                                    join_mark: false,
20599                                    trailing_comments: Vec::new(),
20600                                    span: None,
20601                                    inferred_type: None,
20602                                })
20603                            })
20604                            .collect(),
20605                    }))
20606                } else {
20607                    Expression::Table(Box::new(table))
20608                }
20609            } else {
20610                Expression::Table(Box::new(table))
20611            }
20612        };
20613
20614        // Determine direction: FROM means loading into table, TO means exporting
20615        let kind = self.match_token(TokenType::From);
20616        let has_to = if !kind {
20617            // Try TO keyword for export (TO is a keyword token, not an identifier)
20618            self.match_token(TokenType::To)
20619        } else {
20620            false
20621        };
20622
20623        // Parse source/destination files or stage only if FROM/TO was found
20624        // and we're not at a parameter (which would start with identifier = ...)
20625        let mut files = Vec::new();
20626        if kind
20627            || has_to
20628            || self.check(TokenType::String)
20629            || self.is_stage_reference()
20630            || self.check(TokenType::LParen)
20631        {
20632            // Check for subquery: FROM (SELECT ...)
20633            if self.check(TokenType::LParen) {
20634                // Peek ahead to see if this is a subquery
20635                let start = self.current;
20636                self.skip(); // consume (
20637                let is_select = self.check(TokenType::Select);
20638                self.current = start; // backtrack
20639                if is_select {
20640                    // Parse the subquery
20641                    let subquery = self.parse_primary()?;
20642                    files.push(subquery);
20643                }
20644            }
20645            // Parse file location(s) until we hit a parameter or end
20646            while !self.is_at_end() && !self.check(TokenType::Semicolon) && files.is_empty()
20647                || (self.check(TokenType::Comma) && !files.is_empty())
20648            {
20649                // Consume comma if present (for multiple files)
20650                if !files.is_empty() && !self.match_token(TokenType::Comma) {
20651                    break;
20652                }
20653                // Check if this looks like a parameter (identifier followed by =)
20654                // But stage references (@stage) are not parameters
20655                if (self.check(TokenType::Var) || self.check_keyword())
20656                    && !self.is_stage_reference()
20657                {
20658                    let lookahead = self.current + 1;
20659                    if lookahead < self.tokens.len()
20660                        && self.tokens[lookahead].token_type == TokenType::Eq
20661                    {
20662                        break; // This is a parameter, stop parsing files
20663                    }
20664                }
20665                // Check for WITH keyword - stop parsing files
20666                if self.check(TokenType::With) {
20667                    break;
20668                }
20669                // Stop if we don't see a file location start
20670                // Include QuotedIdentifier for Databricks backtick-quoted paths like `s3://link`
20671                if !self.check(TokenType::String)
20672                    && !self.is_stage_reference()
20673                    && !self.check(TokenType::Var)
20674                    && !self.check_keyword()
20675                    && !self.check(TokenType::QuotedIdentifier)
20676                {
20677                    break;
20678                }
20679                // For COPY INTO ... FROM table_name, handle dotted table references
20680                // If the next token is a Var/Identifier and the one after is a Dot, parse as table reference
20681                if (self.check(TokenType::Var) || self.is_identifier_token())
20682                    && !self.is_stage_reference()
20683                {
20684                    let lookahead = self.current + 1;
20685                    let has_dot = lookahead < self.tokens.len()
20686                        && self.tokens[lookahead].token_type == TokenType::Dot;
20687                    if has_dot {
20688                        let table = self.parse_table_ref()?;
20689                        files.push(Expression::Table(Box::new(table)));
20690                        continue;
20691                    }
20692                }
20693                let location = self.parse_file_location()?;
20694                files.push(location);
20695            }
20696        }
20697
20698        // Parse credentials and parameters
20699        let mut params = Vec::new();
20700        let mut credentials = None;
20701        let mut with_wrapped = false;
20702
20703        // Parse Snowflake-style parameters: KEY = VALUE or KEY = (nested values)
20704        // or DuckDB/PostgreSQL WITH (KEY VALUE, ...) format
20705        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
20706            // Match WITH keyword if present (some dialects use WITH before params)
20707            let had_with = self.match_token(TokenType::With);
20708
20709            // Check for wrapped parameters in parentheses
20710            if self.match_token(TokenType::LParen) {
20711                if had_with {
20712                    with_wrapped = true;
20713                }
20714                while !self.check(TokenType::RParen) && !self.is_at_end() {
20715                    let param = self.parse_copy_parameter()?;
20716                    params.push(param);
20717                    // Consume optional comma between params
20718                    self.match_token(TokenType::Comma);
20719                }
20720                self.expect(TokenType::RParen)?;
20721                break;
20722            }
20723
20724            // Parse individual parameter: NAME = value
20725            if self.check(TokenType::Var) || self.check_keyword() {
20726                let param = self.parse_copy_parameter()?;
20727
20728                // Handle special CREDENTIALS parameter (case-insensitive)
20729                if param.name.eq_ignore_ascii_case("CREDENTIALS") {
20730                    // For Redshift-style CREDENTIALS 'string' (single string value)
20731                    // vs Snowflake-style CREDENTIALS = (KEY='value', KEY2='value')
20732                    if let Some(Expression::Literal(lit)) = &param.value {
20733                        if let Literal::String(s) = lit.as_ref() {
20734                            // Redshift style: store as a simple credentials string
20735                            let creds = Credentials {
20736                                credentials: vec![("".to_string(), s.clone())],
20737                                storage: None,
20738                                encryption: None,
20739                            };
20740                            credentials = Some(Box::new(creds));
20741                        }
20742                    } else {
20743                        // Snowflake style: key=value pairs
20744                        let creds = Credentials {
20745                            credentials: param
20746                                .values
20747                                .iter()
20748                                .filter_map(|v| {
20749                                    if let Expression::Eq(eq) = v {
20750                                        let key = if let Expression::Column(c) = &eq.left {
20751                                            c.name.name.clone()
20752                                        } else {
20753                                            return None;
20754                                        };
20755                                        let val = if let Expression::Literal(lit) = &eq.right {
20756                                            if let Literal::String(s) = lit.as_ref() {
20757                                                s.clone()
20758                                            } else {
20759                                                String::new()
20760                                            }
20761                                        } else {
20762                                            return None;
20763                                        };
20764                                        Some((key, val))
20765                                    } else {
20766                                        None
20767                                    }
20768                                })
20769                                .collect(),
20770                            storage: None,
20771                            encryption: None,
20772                        };
20773                        credentials = Some(Box::new(creds));
20774                    }
20775                } else if param.name.eq_ignore_ascii_case("STORAGE_INTEGRATION") {
20776                    // Store STORAGE_INTEGRATION as a regular parameter only
20777                    // Don't use the credentials.storage field for this
20778                    params.push(param);
20779                } else {
20780                    params.push(param);
20781                }
20782            } else {
20783                break;
20784            }
20785        }
20786
20787        Ok(Expression::Copy(Box::new(CopyStmt {
20788            this,
20789            kind,
20790            files,
20791            params,
20792            credentials,
20793            is_into,
20794            with_wrapped,
20795        })))
20796    }
20797
20798    /// Parse a single COPY parameter: NAME = value, NAME = (nested values), or NAME value (no =)
20799    fn parse_copy_parameter(&mut self) -> Result<CopyParameter> {
20800        // Preserve original case for parameter name (important for Redshift COPY options)
20801        let name = self.expect_identifier_or_keyword()?;
20802
20803        let mut value = None;
20804        let mut values = Vec::new();
20805
20806        let has_eq = self.match_token(TokenType::Eq);
20807
20808        if has_eq {
20809            if self.match_token(TokenType::LParen) {
20810                // Nested parameter list: KEY = (nested_key=value, ...) or KEY = (value1, value2)
20811                // Check if this is a list of simple values (like strings) or key=value pairs
20812                // If the first token is a string/number, it's a list of values
20813                if self.check(TokenType::String) || self.check(TokenType::Number) {
20814                    // Simple value list: FILES = ('test1.csv', 'test2.csv')
20815                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20816                        values.push(self.parse_primary()?);
20817                        if !self.match_token(TokenType::Comma) {
20818                            break;
20819                        }
20820                    }
20821                } else {
20822                    // Key=value pairs: CREDENTIALS = (AWS_KEY_ID='id' AWS_SECRET_KEY='key')
20823                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20824                        // Parse nested key=value pairs
20825                        let nested_key = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
20826                        if self.match_token(TokenType::Eq) {
20827                            let nested_value = self.parse_copy_param_value()?;
20828                            // Create an Eq expression for the nested key=value
20829                            values.push(Expression::Eq(Box::new(BinaryOp {
20830                                left: Expression::boxed_column(Column {
20831                                    name: Identifier::new(nested_key),
20832                                    table: None,
20833                                    join_mark: false,
20834                                    trailing_comments: Vec::new(),
20835                                    span: None,
20836                                    inferred_type: None,
20837                                }),
20838                                right: nested_value,
20839                                left_comments: Vec::new(),
20840                                operator_comments: Vec::new(),
20841                                trailing_comments: Vec::new(),
20842                                inferred_type: None,
20843                            })));
20844                        } else {
20845                            // Just a keyword/value without =
20846                            values.push(Expression::boxed_column(Column {
20847                                name: Identifier::new(nested_key),
20848                                table: None,
20849                                join_mark: false,
20850                                trailing_comments: Vec::new(),
20851                                span: None,
20852                                inferred_type: None,
20853                            }));
20854                        }
20855                        // Consume optional comma between nested values
20856                        self.match_token(TokenType::Comma);
20857                    }
20858                }
20859                self.expect(TokenType::RParen)?;
20860            } else {
20861                // Simple value: KEY = value
20862                value = Some(self.parse_copy_param_value()?);
20863            }
20864        } else {
20865            // No = sign: DuckDB/PostgreSQL format (KEY value or KEY (col1, col2))
20866            // Check if followed by a value: string, number, boolean, identifier, or tuple
20867            if self.check(TokenType::LParen) {
20868                // Check if this is a COPY_INTO_VARLEN_OPTIONS parameter
20869                // These are Databricks/Snowflake options that contain key='value' pairs without = before (
20870                let is_varlen_option = matches!(
20871                    name.as_str(),
20872                    "FORMAT_OPTIONS" | "COPY_OPTIONS" | "FILE_FORMAT" | "CREDENTIAL"
20873                );
20874
20875                self.skip(); // consume (
20876
20877                if is_varlen_option {
20878                    // Parse as key='value' pairs: FORMAT_OPTIONS ('opt1'='true', 'opt2'='test')
20879                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20880                        if self.check(TokenType::String) {
20881                            // Parse 'key'='value' pair
20882                            let key_token = self.advance();
20883                            let key = key_token.text.clone();
20884                            if self.match_token(TokenType::Eq) {
20885                                let val = self.parse_copy_param_value()?;
20886                                values.push(Expression::Eq(Box::new(BinaryOp {
20887                                    left: Expression::Literal(Box::new(Literal::String(key))),
20888                                    right: val,
20889                                    left_comments: Vec::new(),
20890                                    operator_comments: Vec::new(),
20891                                    trailing_comments: Vec::new(),
20892                                    inferred_type: None,
20893                                })));
20894                            } else {
20895                                // Just a string without =
20896                                values.push(Expression::Literal(Box::new(Literal::String(key))));
20897                            }
20898                        } else if self.check(TokenType::Var)
20899                            || self.check_keyword()
20900                            || self.is_identifier_token()
20901                        {
20902                            // Parse identifier='value' pair (unquoted key)
20903                            let key = self.advance().text.clone();
20904                            if self.match_token(TokenType::Eq) {
20905                                let val = self.parse_copy_param_value()?;
20906                                values.push(Expression::Eq(Box::new(BinaryOp {
20907                                    left: Expression::boxed_column(Column {
20908                                        name: Identifier::new(key),
20909                                        table: None,
20910                                        join_mark: false,
20911                                        trailing_comments: Vec::new(),
20912                                        span: None,
20913                                        inferred_type: None,
20914                                    }),
20915                                    right: val,
20916                                    left_comments: Vec::new(),
20917                                    operator_comments: Vec::new(),
20918                                    trailing_comments: Vec::new(),
20919                                    inferred_type: None,
20920                                })));
20921                            } else {
20922                                // Just an identifier without =
20923                                values.push(Expression::boxed_column(Column {
20924                                    name: Identifier::new(key),
20925                                    table: None,
20926                                    join_mark: false,
20927                                    trailing_comments: Vec::new(),
20928                                    span: None,
20929                                    inferred_type: None,
20930                                }));
20931                            }
20932                        } else {
20933                            break;
20934                        }
20935                        self.match_token(TokenType::Comma);
20936                    }
20937                } else {
20938                    // Tuple value: FORCE_NOT_NULL (col1, col2)
20939                    let mut items = Vec::new();
20940                    while !self.check(TokenType::RParen) && !self.is_at_end() {
20941                        items.push(self.parse_primary()?);
20942                        if !self.match_token(TokenType::Comma) {
20943                            break;
20944                        }
20945                    }
20946                    value = Some(Expression::Tuple(Box::new(Tuple { expressions: items })));
20947                }
20948                self.expect(TokenType::RParen)?;
20949            } else if self.check(TokenType::LBrace) {
20950                // Map literal: KV_METADATA {'key': 'value', ...}
20951                value = Some(self.parse_primary()?);
20952            } else if self.check(TokenType::String) || self.check(TokenType::Number) {
20953                // String or number value
20954                value = Some(self.parse_copy_param_value()?);
20955            } else if self.check(TokenType::True) || self.check(TokenType::False) {
20956                // Boolean value (TRUE/FALSE are keyword tokens)
20957                value = Some(self.parse_copy_param_value()?);
20958            } else if !self.check(TokenType::Comma)
20959                && !self.check(TokenType::RParen)
20960                && !self.is_at_end()
20961                && !self.check(TokenType::Semicolon)
20962            {
20963                // Identifier value: FORMAT JSON, HEADER MATCH, etc.
20964                // But skip if this is a known flag-only parameter (Redshift COPY options that take no value)
20965                let name_upper = name.to_ascii_uppercase();
20966                let is_flag_param = matches!(
20967                    name_upper.as_str(),
20968                    "EMPTYASNULL"
20969                        | "BLANKSASNULL"
20970                        | "ACCEPTINVCHARS"
20971                        | "COMPUPDATE"
20972                        | "STATUPDATE"
20973                        | "NOLOAD"
20974                        | "ESCAPE"
20975                        | "REMOVEQUOTES"
20976                        | "EXPLICIT_IDS"
20977                        | "FILLRECORD"
20978                        | "TRIMBLANKS"
20979                        | "TRUNCATECOLUMNS"
20980                        | "ROUNDEC"
20981                        | "IGNOREHEADER"
20982                        | "IGNOREBLANKLINES"
20983                        | "ACCEPTANYDATE"
20984                );
20985                if !is_flag_param && (self.check(TokenType::Var) || self.check_keyword()) {
20986                    value = Some(self.parse_copy_param_value()?);
20987                }
20988            }
20989            // If nothing matched, it's a bare flag parameter with no value (allowed)
20990        }
20991
20992        Ok(CopyParameter {
20993            name,
20994            value,
20995            values,
20996            eq: has_eq,
20997        })
20998    }
20999
21000    /// Parse a value for COPY parameters (handles strings, identifiers, numbers, lists)
21001    fn parse_copy_param_value(&mut self) -> Result<Expression> {
21002        // Handle lists like ('file1', 'file2')
21003        if self.match_token(TokenType::LParen) {
21004            let mut items = Vec::new();
21005            while !self.check(TokenType::RParen) && !self.is_at_end() {
21006                items.push(self.parse_primary()?);
21007                if !self.match_token(TokenType::Comma) {
21008                    break;
21009                }
21010            }
21011            self.expect(TokenType::RParen)?;
21012            return Ok(Expression::Tuple(Box::new(Tuple { expressions: items })));
21013        }
21014
21015        // Handle string, number, boolean, identifier
21016        if self.check(TokenType::String) {
21017            let token = self.advance();
21018            return Ok(Expression::Literal(Box::new(Literal::String(
21019                token.text.clone(),
21020            ))));
21021        }
21022        // Handle quoted identifier (e.g., STORAGE_INTEGRATION = "storage")
21023        if self.check(TokenType::QuotedIdentifier) {
21024            let token = self.advance();
21025            return Ok(Expression::boxed_column(Column {
21026                name: Identifier::quoted(token.text.clone()),
21027                table: None,
21028                join_mark: false,
21029                trailing_comments: Vec::new(),
21030                span: None,
21031                inferred_type: None,
21032            }));
21033        }
21034        if self.check(TokenType::Number) {
21035            let token = self.advance();
21036            return Ok(Expression::Literal(Box::new(Literal::Number(
21037                token.text.clone(),
21038            ))));
21039        }
21040        if self.match_token(TokenType::True) {
21041            return Ok(Expression::Boolean(BooleanLiteral { value: true }));
21042        }
21043        if self.match_token(TokenType::False) {
21044            return Ok(Expression::Boolean(BooleanLiteral { value: false }));
21045        }
21046        // Identifier (e.g., FORMAT_NAME=my_format)
21047        if self.check(TokenType::Var) || self.check_keyword() {
21048            // Could be a qualified name like MY_DATABASE.MY_SCHEMA.MY_FORMAT
21049            let first = self.advance().text.clone();
21050            if self.match_token(TokenType::Dot) {
21051                let second = self.expect_identifier_or_keyword()?;
21052                if self.match_token(TokenType::Dot) {
21053                    let third = self.expect_identifier_or_keyword()?;
21054                    return Ok(Expression::boxed_column(Column {
21055                        name: Identifier::new(format!("{}.{}.{}", first, second, third)),
21056                        table: None,
21057                        join_mark: false,
21058                        trailing_comments: Vec::new(),
21059                        span: None,
21060                        inferred_type: None,
21061                    }));
21062                }
21063                return Ok(Expression::boxed_column(Column {
21064                    name: Identifier::new(format!("{}.{}", first, second)),
21065                    table: None,
21066                    join_mark: false,
21067                    trailing_comments: Vec::new(),
21068                    span: None,
21069                    inferred_type: None,
21070                }));
21071            }
21072            return Ok(Expression::boxed_column(Column {
21073                name: Identifier::new(first),
21074                table: None,
21075                join_mark: false,
21076                trailing_comments: Vec::new(),
21077                span: None,
21078                inferred_type: None,
21079            }));
21080        }
21081
21082        Err(self.parse_error("Expected value for COPY parameter"))
21083    }
21084
21085    /// Parse Snowflake stage reference when tokenized as String (e.g., '@mystage', '@external/location')
21086    /// Handles: '@mystage', '@external/location'
21087    fn parse_stage_reference_from_string(&mut self) -> Result<Expression> {
21088        use crate::expressions::StageReference;
21089
21090        // The String token contains @ and the entire path
21091        let string_token = self.advance();
21092        let full_path = string_token.text.clone();
21093
21094        // Split on / to get stage name and path
21095        let parts: Vec<&str> = full_path.splitn(2, '/').collect();
21096        let name = parts[0].to_string();
21097        let path = if parts.len() > 1 {
21098            Some(format!("/{}", parts[1]))
21099        } else {
21100            None
21101        };
21102
21103        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
21104        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
21105            let mut ff = None;
21106            let mut pat = None;
21107
21108            loop {
21109                if self.match_identifier("FILE_FORMAT") {
21110                    self.expect(TokenType::FArrow)?; // =>
21111                    ff = Some(self.parse_primary()?);
21112                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
21113                    // PATTERN can be tokenized as keyword or identifier
21114                    self.expect(TokenType::FArrow)?; // =>
21115                    if let Expression::Literal(lit) = self.parse_primary()? {
21116                        if let Literal::String(s) = lit.as_ref() {
21117                            pat = Some(s.clone());
21118                        }
21119                    }
21120                } else {
21121                    break;
21122                }
21123
21124                if !self.match_token(TokenType::Comma) {
21125                    break;
21126                }
21127            }
21128
21129            self.expect(TokenType::RParen)?;
21130            (ff, pat)
21131        } else {
21132            (None, None)
21133        };
21134
21135        Ok(Expression::StageReference(Box::new(StageReference {
21136            name,
21137            path,
21138            file_format,
21139            pattern,
21140            quoted: true, // Stage reference came from a quoted string
21141        })))
21142    }
21143
21144    /// Parse Snowflake stage reference when tokenized as Var (e.g., @mystage becomes Var token)
21145    /// Handles: @mystage, @mystage/path/to/file.csv
21146    fn parse_stage_reference_from_var(&mut self) -> Result<Expression> {
21147        use crate::expressions::StageReference;
21148
21149        // The Var token already contains @ and the stage name
21150        let var_token = self.advance();
21151        let mut name = var_token.text.clone();
21152
21153        // Handle qualified names: @namespace.stage
21154        while self.match_token(TokenType::Dot) {
21155            name.push('.');
21156            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
21157                name.push_str(&self.advance().text);
21158            } else if self.check(TokenType::Percent) {
21159                // Handle table stage in qualified path: @namespace.%table_name
21160                self.skip();
21161                name.push('%');
21162                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
21163                    name.push_str(&self.advance().text);
21164                }
21165            } else {
21166                break;
21167            }
21168        }
21169
21170        // Handle path after stage: @stage/path/to/file.csv
21171        let path = if self.match_token(TokenType::Slash) {
21172            let mut path_str = String::from("/");
21173            // Consume path components until we hit whitespace/paren/etc.
21174            while !self.is_at_end() {
21175                if self.check(TokenType::Identifier)
21176                    || self.check(TokenType::Var)
21177                    || self.check(TokenType::Number)
21178                    || self.check(TokenType::Dot)
21179                    || self.check(TokenType::Dash)
21180                    || self.check(TokenType::Star)
21181                    || self.check(TokenType::To)
21182                    || self.is_safe_keyword_as_identifier()
21183                {
21184                    path_str.push_str(&self.advance().text);
21185                } else if self.match_token(TokenType::Slash) {
21186                    path_str.push('/');
21187                } else {
21188                    break;
21189                }
21190            }
21191            Some(path_str)
21192        } else {
21193            None
21194        };
21195
21196        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
21197        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
21198            let mut ff = None;
21199            let mut pat = None;
21200
21201            loop {
21202                if self.match_identifier("FILE_FORMAT") {
21203                    self.expect(TokenType::FArrow)?; // =>
21204                    ff = Some(self.parse_primary()?);
21205                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
21206                    // PATTERN can be tokenized as keyword or identifier
21207                    self.expect(TokenType::FArrow)?; // =>
21208                    if let Expression::Literal(lit) = self.parse_primary()? {
21209                        if let Literal::String(s) = lit.as_ref() {
21210                            pat = Some(s.clone());
21211                        }
21212                    }
21213                } else {
21214                    break;
21215                }
21216
21217                if !self.match_token(TokenType::Comma) {
21218                    break;
21219                }
21220            }
21221
21222            self.expect(TokenType::RParen)?;
21223            (ff, pat)
21224        } else {
21225            (None, None)
21226        };
21227
21228        Ok(Expression::StageReference(Box::new(StageReference {
21229            name,
21230            path,
21231            file_format,
21232            pattern,
21233            quoted: false,
21234        })))
21235    }
21236
21237    /// Parse Snowflake stage reference in FROM clause
21238    /// Handles: @stage, @"stage", @namespace.stage, @stage/path/file.csv, @~, @%table
21239    fn parse_stage_reference(&mut self) -> Result<Expression> {
21240        use crate::expressions::StageReference;
21241
21242        self.expect(TokenType::DAt)?; // consume @
21243
21244        // Build the stage name - can include dots, slashes, etc.
21245        let mut name = String::from("@");
21246
21247        // Handle special stage types:
21248        // @~ = user stage
21249        // @% = table stage (followed by table name)
21250        if self.check(TokenType::Tilde) {
21251            self.skip();
21252            name.push('~');
21253        } else if self.check(TokenType::Percent) {
21254            self.skip();
21255            name.push('%');
21256            // Table name follows (can be qualified: schema.table)
21257            loop {
21258                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
21259                    name.push_str(&self.advance().text);
21260                } else {
21261                    break;
21262                }
21263                // Handle qualified table names: %db.schema.table
21264                if self.match_token(TokenType::Dot) {
21265                    name.push('.');
21266                } else {
21267                    break;
21268                }
21269            }
21270        } else {
21271            // Handle quoted or unquoted stage names
21272            loop {
21273                if self.check(TokenType::QuotedIdentifier) {
21274                    // Preserve quotes for quoted identifiers
21275                    let text = self.advance().text;
21276                    name.push('"');
21277                    name.push_str(&text);
21278                    name.push('"');
21279                } else if self.check(TokenType::Percent) {
21280                    // Handle table stage in qualified path: @namespace.%table_name
21281                    self.skip();
21282                    name.push('%');
21283                    if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
21284                        name.push_str(&self.advance().text);
21285                    }
21286                } else if self.check(TokenType::Identifier)
21287                    || self.check(TokenType::Var)
21288                    || self.is_safe_keyword_as_identifier()
21289                {
21290                    name.push_str(&self.advance().text);
21291                } else {
21292                    break;
21293                }
21294
21295                // Handle dots for qualified names: @namespace.stage or @"schema"."stage"
21296                if self.match_token(TokenType::Dot) {
21297                    name.push('.');
21298                } else {
21299                    break;
21300                }
21301            }
21302        }
21303
21304        // Handle path after stage: @stage/path/to/file.csv
21305        let path = if self.match_token(TokenType::Slash) {
21306            let mut path_str = String::from("/");
21307            // Consume path components until we hit whitespace/paren/etc.
21308            // Note: path can include keywords like 'to', 'data', etc.
21309            while !self.is_at_end() {
21310                if self.check(TokenType::Identifier)
21311                    || self.check(TokenType::Var)
21312                    || self.check(TokenType::Number)
21313                    || self.check(TokenType::Dot)
21314                    || self.check(TokenType::Dash)
21315                    || self.check(TokenType::Star)
21316                    || self.check(TokenType::To)
21317                    || self.is_safe_keyword_as_identifier()
21318                {
21319                    path_str.push_str(&self.advance().text);
21320                } else if self.match_token(TokenType::Slash) {
21321                    path_str.push('/');
21322                } else {
21323                    break;
21324                }
21325            }
21326            Some(path_str)
21327        } else {
21328            None
21329        };
21330
21331        // Handle optional parameters: (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
21332        let (file_format, pattern) = if self.match_token(TokenType::LParen) {
21333            let mut ff = None;
21334            let mut pat = None;
21335
21336            loop {
21337                if self.match_identifier("FILE_FORMAT") {
21338                    self.expect(TokenType::FArrow)?; // =>
21339                    ff = Some(self.parse_primary()?);
21340                } else if self.match_identifier("PATTERN") || self.match_token(TokenType::Pattern) {
21341                    // PATTERN can be tokenized as keyword or identifier
21342                    self.expect(TokenType::FArrow)?; // =>
21343                    if let Expression::Literal(lit) = self.parse_primary()? {
21344                        if let Literal::String(s) = lit.as_ref() {
21345                            pat = Some(s.clone());
21346                        }
21347                    }
21348                } else {
21349                    break;
21350                }
21351
21352                if !self.match_token(TokenType::Comma) {
21353                    break;
21354                }
21355            }
21356
21357            self.expect(TokenType::RParen)?;
21358            (ff, pat)
21359        } else {
21360            (None, None)
21361        };
21362
21363        Ok(Expression::StageReference(Box::new(StageReference {
21364            name,
21365            path,
21366            file_format,
21367            pattern,
21368            quoted: false,
21369        })))
21370    }
21371
21372    /// Parse file location for COPY/PUT statements
21373    /// Handles: @stage, @db.schema.stage, @stage/path, 's3://bucket/path', file:///path
21374    fn parse_file_location(&mut self) -> Result<Expression> {
21375        // Stage reference starting with @ (tokenized as DAt or as a Var starting with @)
21376        if self.check(TokenType::DAt) {
21377            self.skip(); // consume @
21378            let mut stage_path = String::from("@");
21379
21380            // Handle table stage prefix: @%table
21381            if self.check(TokenType::Percent) || self.check(TokenType::Mod) {
21382                stage_path.push('%');
21383                self.skip(); // consume %
21384            }
21385            // Handle user stage: @~
21386            else if self.check(TokenType::Tilde) {
21387                stage_path.push('~');
21388                self.skip(); // consume ~
21389            }
21390
21391            // Get stage name
21392            if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token() {
21393                stage_path.push_str(&self.advance().text);
21394            }
21395            // Parse qualified name parts: .schema.stage
21396            while self.check(TokenType::Dot) {
21397                self.skip(); // consume .
21398                stage_path.push('.');
21399                if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token()
21400                {
21401                    stage_path.push_str(&self.advance().text);
21402                }
21403            }
21404            // Parse path after stage: /path/to/file.csv
21405            // Consume all connected path components (dots, dashes, numbers, etc.)
21406            // matching the logic in parse_stage_reference.
21407            if self.match_token(TokenType::Slash) {
21408                stage_path.push('/');
21409                while !self.is_at_end() {
21410                    if (self.check(TokenType::Var)
21411                        || self.check(TokenType::Identifier)
21412                        || self.check(TokenType::Number)
21413                        || self.check(TokenType::Dot)
21414                        || self.check(TokenType::Dash)
21415                        || self.check(TokenType::Star)
21416                        || self.check(TokenType::To)
21417                        || self.is_safe_keyword_as_identifier())
21418                        && !self.check_next(TokenType::Eq)
21419                    {
21420                        stage_path.push_str(&self.advance().text);
21421                    } else if self.match_token(TokenType::Slash) {
21422                        stage_path.push('/');
21423                    } else {
21424                        break;
21425                    }
21426                }
21427            }
21428            return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21429        }
21430
21431        // Stage reference tokenized as a Var starting with @ (e.g., @random_stage)
21432        // This happens when the tokenizer combines @ with the following identifier
21433        if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
21434            let mut stage_path = self.advance().text.clone();
21435            // Parse qualified name parts: .schema.stage
21436            while self.check(TokenType::Dot) {
21437                self.skip(); // consume .
21438                stage_path.push('.');
21439                if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token()
21440                {
21441                    stage_path.push_str(&self.advance().text);
21442                }
21443            }
21444            // Parse path after stage: /path/to/file.csv
21445            if self.match_token(TokenType::Slash) {
21446                stage_path.push('/');
21447                while !self.is_at_end() {
21448                    if (self.check(TokenType::Var)
21449                        || self.check(TokenType::Identifier)
21450                        || self.check(TokenType::Number)
21451                        || self.check(TokenType::Dot)
21452                        || self.check(TokenType::Dash)
21453                        || self.check(TokenType::Star)
21454                        || self.check(TokenType::To)
21455                        || self.is_safe_keyword_as_identifier())
21456                        && !self.check_next(TokenType::Eq)
21457                    {
21458                        stage_path.push_str(&self.advance().text);
21459                    } else if self.match_token(TokenType::Slash) {
21460                        stage_path.push('/');
21461                    } else {
21462                        break;
21463                    }
21464                }
21465            }
21466            return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21467        }
21468
21469        // String literal (file path or URL)
21470        if self.check(TokenType::String) {
21471            let token = self.advance();
21472            return Ok(Expression::Literal(Box::new(Literal::String(
21473                token.text.clone(),
21474            ))));
21475        }
21476
21477        // Backtick-quoted identifier (Databricks style: `s3://link`)
21478        if self.check(TokenType::QuotedIdentifier) {
21479            let token = self.advance();
21480            return Ok(Expression::Identifier(Identifier::quoted(
21481                token.text.clone(),
21482            )));
21483        }
21484
21485        // Identifier (could be a stage name without @)
21486        if self.check(TokenType::Var) || self.check_keyword() {
21487            let ident = self.advance().text.clone();
21488            return Ok(Expression::boxed_column(Column {
21489                name: Identifier::new(ident),
21490                table: None,
21491                join_mark: false,
21492                trailing_comments: Vec::new(),
21493                span: None,
21494                inferred_type: None,
21495            }));
21496        }
21497
21498        Err(self.parse_error("Expected file location"))
21499    }
21500
21501    /// Parse Snowflake stage reference as a string for PUT/GET/COPY statements
21502    /// Handles: @stage, @%table, @~, @db.schema.stage, @"quoted"."stage", @stage/path
21503    /// Returns a Literal::String containing the stage path
21504    fn parse_stage_reference_as_string(&mut self) -> Result<Expression> {
21505        // Stage reference starting with @ (tokenized as DAt)
21506        if self.check(TokenType::DAt) {
21507            self.skip(); // consume @
21508            let mut stage_path = String::from("@");
21509
21510            // Handle table stage prefix: @%table
21511            if self.check(TokenType::Percent) || self.check(TokenType::Mod) {
21512                stage_path.push('%');
21513                self.skip(); // consume %
21514            }
21515            // Handle user stage: @~
21516            else if self.check(TokenType::Tilde) {
21517                stage_path.push('~');
21518                self.skip(); // consume ~
21519                             // After @~, parse any path segments
21520                while self.check(TokenType::Slash) {
21521                    self.skip(); // consume /
21522                    stage_path.push('/');
21523                    if (self.check(TokenType::Var)
21524                        || self.check_keyword()
21525                        || self.is_identifier_token())
21526                        && !self.check_next(TokenType::Eq)
21527                    {
21528                        stage_path.push_str(&self.advance().text);
21529                    }
21530                }
21531                return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21532            }
21533
21534            // Get stage name (could be quoted identifier)
21535            if self.check(TokenType::QuotedIdentifier) {
21536                // Preserve quoted identifier with quotes
21537                let text = &self.peek().text;
21538                stage_path.push('"');
21539                stage_path.push_str(text);
21540                stage_path.push('"');
21541                self.skip();
21542            } else if self.check(TokenType::Var)
21543                || self.check_keyword()
21544                || self.check(TokenType::Identifier)
21545            {
21546                stage_path.push_str(&self.advance().text);
21547            }
21548
21549            // Parse qualified name parts: .schema.stage (may include quoted identifiers)
21550            while self.check(TokenType::Dot) {
21551                self.skip(); // consume .
21552                stage_path.push('.');
21553                if self.check(TokenType::QuotedIdentifier) {
21554                    // Preserve quoted identifier with quotes
21555                    let text = &self.peek().text;
21556                    stage_path.push('"');
21557                    stage_path.push_str(text);
21558                    stage_path.push('"');
21559                    self.skip();
21560                } else if self.check(TokenType::Var)
21561                    || self.check_keyword()
21562                    || self.check(TokenType::Identifier)
21563                {
21564                    stage_path.push_str(&self.advance().text);
21565                }
21566            }
21567
21568            // Parse path segments: /path/to/file
21569            while self.check(TokenType::Slash) {
21570                self.skip(); // consume /
21571                stage_path.push('/');
21572                // Get path segment but don't consume if followed by = (that's a parameter)
21573                if (self.check(TokenType::Var)
21574                    || self.check_keyword()
21575                    || self.is_identifier_token())
21576                    && !self.check_next(TokenType::Eq)
21577                {
21578                    stage_path.push_str(&self.advance().text);
21579                }
21580            }
21581            return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21582        }
21583
21584        // Stage reference tokenized as a Var starting with @ (e.g., @s1)
21585        if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
21586            let mut stage_path = self.advance().text.clone();
21587
21588            // Parse qualified name parts: .schema.stage (may include quoted identifiers)
21589            while self.check(TokenType::Dot) {
21590                self.skip(); // consume .
21591                stage_path.push('.');
21592                if self.check(TokenType::QuotedIdentifier) {
21593                    let text = &self.peek().text;
21594                    stage_path.push('"');
21595                    stage_path.push_str(text);
21596                    stage_path.push('"');
21597                    self.skip();
21598                } else if self.check(TokenType::Var)
21599                    || self.check_keyword()
21600                    || self.check(TokenType::Identifier)
21601                {
21602                    stage_path.push_str(&self.advance().text);
21603                }
21604            }
21605
21606            // Parse path segments: /path/to/file
21607            while self.check(TokenType::Slash) {
21608                self.skip(); // consume /
21609                stage_path.push('/');
21610                if (self.check(TokenType::Var)
21611                    || self.check_keyword()
21612                    || self.is_identifier_token())
21613                    && !self.check_next(TokenType::Eq)
21614                {
21615                    stage_path.push_str(&self.advance().text);
21616                }
21617            }
21618            return Ok(Expression::Literal(Box::new(Literal::String(stage_path))));
21619        }
21620
21621        Err(self.parse_error("Expected stage reference starting with @"))
21622    }
21623
21624    /// Parse PUT statement (Snowflake)
21625    /// PUT file://<path> @<stage> [AUTO_COMPRESS = TRUE|FALSE] ...
21626    fn parse_put(&mut self) -> Result<Expression> {
21627        self.expect(TokenType::Put)?;
21628
21629        // Parse source file path (usually file:///path/to/file)
21630        let (source, source_quoted) = if self.check(TokenType::String) {
21631            (self.advance().text.clone(), true)
21632        } else {
21633            // Handle file://path syntax (parsed as identifier + colon + etc.)
21634            // Stop when we see @ (start of stage reference)
21635            let mut source_parts = Vec::new();
21636            while !self.is_at_end() {
21637                // Stop if we see @ (DAt token or Var starting with @)
21638                if self.check(TokenType::DAt) {
21639                    break;
21640                }
21641                if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
21642                    break;
21643                }
21644                let token = self.advance();
21645                source_parts.push(token.text.clone());
21646            }
21647            (source_parts.join(""), false)
21648        };
21649
21650        // Parse target stage (@stage_name)
21651        let target = self.parse_stage_reference_as_string()?;
21652
21653        // Parse optional parameters
21654        // Note: Some parameter names like OVERWRITE are keywords, so we check for those explicitly
21655        // Preserve original casing for identity tests
21656        let mut params = Vec::new();
21657        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21658            let is_param_name = self.check(TokenType::Var)
21659                || self.check_keyword()
21660                || self.check(TokenType::Overwrite);
21661            if is_param_name {
21662                let name = self.advance().text.clone();
21663                let value = if self.match_token(TokenType::Eq) {
21664                    Some(self.parse_primary()?)
21665                } else {
21666                    None
21667                };
21668                params.push(CopyParameter {
21669                    name,
21670                    value,
21671                    values: Vec::new(),
21672                    eq: true,
21673                });
21674            } else {
21675                break;
21676            }
21677        }
21678
21679        Ok(Expression::Put(Box::new(PutStmt {
21680            source,
21681            source_quoted,
21682            target,
21683            params,
21684        })))
21685    }
21686
21687    /// Helper to join command tokens with smart spacing
21688    /// Preserves the structure of file paths, stage references, etc.
21689    fn join_command_tokens(&self, tokens: Vec<(String, TokenType)>) -> String {
21690        let mut result = String::new();
21691        let mut prev_token_type: Option<TokenType> = None;
21692        let mut prev_prev_token_type: Option<TokenType> = None;
21693
21694        for (i, (text, token_type)) in tokens.iter().enumerate() {
21695            let needs_space = if result.is_empty() {
21696                false
21697            } else {
21698                match (prev_token_type, *token_type) {
21699                    // No space after @ (stage references: @stage, @%, @~)
21700                    (Some(TokenType::DAt), _) => false,
21701                    // No space around dots (identifiers: a.b.c)
21702                    (Some(TokenType::Dot), _) => false,
21703                    (_, TokenType::Dot) => false,
21704                    // No space around parentheses
21705                    (Some(TokenType::LParen), _) => false,
21706                    (_, TokenType::LParen) => false,
21707                    (_, TokenType::RParen) => false,
21708                    // No space around square brackets (array access: arr[i])
21709                    (Some(TokenType::LBracket), _) => false,
21710                    (_, TokenType::LBracket) => false,
21711                    (_, TokenType::RBracket) => false,
21712                    // No space before ,
21713                    (_, TokenType::Comma) => false,
21714                    // No space around / (paths: @s1/test)
21715                    (Some(TokenType::Slash), _) => false,
21716                    (_, TokenType::Slash) => false,
21717                    // No space around : (file://path)
21718                    (Some(TokenType::Colon), _) => false,
21719                    (_, TokenType::Colon) => false,
21720                    // No space around % (table stage: @%table)
21721                    (Some(TokenType::Mod), _) => false,
21722                    (_, TokenType::Mod) => false,
21723                    (Some(TokenType::Percent), _) => false,
21724                    (_, TokenType::Percent) => false,
21725                    // Handle = contextually:
21726                    // - No space around = in simple KEY=VALUE patterns where value is terminal
21727                    //   (PARALLEL=1, ENABLED=TRUE, FILE_FORMAT='csv')
21728                    // - Keep space for expressions like SET x = x + 1
21729                    (Some(TokenType::Var), TokenType::Eq) => {
21730                        // If the var starts with @ (parameter like @id = 123), always use spaces
21731                        if i >= 1 && tokens[i - 1].0.starts_with('@') {
21732                            true
21733                        } else if i + 1 < tokens.len() {
21734                            // Check what follows: Var=Number where number is terminal (end or followed by Var)
21735                            let next_type = tokens[i + 1].1;
21736                            // Is the value terminal (end of tokens, or followed by another Var=... pattern)?
21737                            let is_terminal_value =
21738                                i + 2 >= tokens.len() || tokens[i + 2].1 == TokenType::Var;
21739                            match next_type {
21740                                // No space for terminal numbers/bools: PARALLEL=1, ENABLED=TRUE
21741                                // Return false (no space) when terminal
21742                                TokenType::Number | TokenType::True | TokenType::False => {
21743                                    !is_terminal_value
21744                                }
21745                                // No space for terminal strings: FILE_FORMAT='csv'
21746                                TokenType::String => !is_terminal_value,
21747                                // Always space if followed by Var (SET x = y ...)
21748                                _ => true,
21749                            }
21750                        } else {
21751                            true
21752                        }
21753                    }
21754                    // No space after = in terminal KEY=VALUE patterns
21755                    (Some(TokenType::Eq), TokenType::Number)
21756                    | (Some(TokenType::Eq), TokenType::True)
21757                    | (Some(TokenType::Eq), TokenType::False)
21758                    | (Some(TokenType::Eq), TokenType::String) => {
21759                        // Is this a terminal value (end or followed by another Var=...)?
21760                        let is_terminal =
21761                            i + 1 >= tokens.len() || tokens[i + 1].1 == TokenType::Var;
21762                        match prev_prev_token_type {
21763                            // No space (return false) when terminal, space otherwise
21764                            // But always space if the var before = was preceded by @ (parameter)
21765                            Some(TokenType::Var) => {
21766                                // Always space if the var before = starts with @ (parameter)
21767                                if i >= 2 && tokens[i - 2].0.starts_with('@') {
21768                                    true
21769                                } else {
21770                                    !is_terminal
21771                                }
21772                            }
21773                            _ => true, // Space for other cases
21774                        }
21775                    }
21776                    // Always space after = when followed by Var (SET x = y, could be expression)
21777                    (Some(TokenType::Eq), TokenType::Var) => true,
21778                    // No space around :: (cast)
21779                    (Some(TokenType::DColon), _) => false,
21780                    (_, TokenType::DColon) => false,
21781                    // Default: add space
21782                    _ => true,
21783                }
21784            };
21785
21786            if needs_space {
21787                result.push(' ');
21788            }
21789            result.push_str(text);
21790            prev_prev_token_type = prev_token_type;
21791            prev_token_type = Some(*token_type);
21792        }
21793        result
21794    }
21795
21796    /// Join Teradata table option tokens with Teradata-specific spacing
21797    /// - No spaces around '='
21798    /// - No spaces around dots or parentheses
21799    /// - Space-separated words otherwise
21800    fn join_teradata_option_tokens(&self, tokens: Vec<(String, TokenType)>) -> String {
21801        let mut result = String::new();
21802        let mut prev_token_type: Option<TokenType> = None;
21803
21804        for (text, token_type) in tokens {
21805            let needs_space = if result.is_empty() {
21806                false
21807            } else {
21808                match (prev_token_type, token_type) {
21809                    (Some(TokenType::Dot), _) => false,
21810                    (_, TokenType::Dot) => false,
21811                    (Some(TokenType::LParen), _) => false,
21812                    (_, TokenType::LParen) => false,
21813                    (_, TokenType::RParen) => false,
21814                    (_, TokenType::Comma) => false,
21815                    (Some(TokenType::Eq), _) => false,
21816                    (_, TokenType::Eq) => false,
21817                    _ => true,
21818                }
21819            };
21820
21821            if needs_space {
21822                result.push(' ');
21823            }
21824            result.push_str(&text);
21825            prev_token_type = Some(token_type);
21826        }
21827
21828        result
21829    }
21830
21831    /// Parse RM or REMOVE command (Snowflake)
21832    /// RM @stage_name / REMOVE @stage_name
21833    fn parse_rm_command(&mut self) -> Result<Expression> {
21834        let command_token = self.advance(); // RM or REMOVE
21835        let command_name = command_token.text.to_ascii_uppercase();
21836
21837        // Collect remaining tokens with their types
21838        let mut tokens = vec![(command_name, command_token.token_type)];
21839        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21840            let token = self.advance();
21841            tokens.push((token.text.clone(), token.token_type));
21842        }
21843
21844        Ok(Expression::Command(Box::new(Command {
21845            this: self.join_command_tokens(tokens),
21846        })))
21847    }
21848
21849    /// Parse GET command (Snowflake)
21850    /// GET @stage_name 'file:///path'
21851    fn parse_get_command(&mut self) -> Result<Expression> {
21852        let get_token = self.advance(); // consume GET (it's already matched)
21853
21854        // Collect remaining tokens with their types, preserving quotes
21855        let mut tokens = vec![("GET".to_string(), get_token.token_type)];
21856        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21857            let token = self.advance();
21858            // Re-add quotes around string and quoted identifier tokens
21859            let text = match token.token_type {
21860                TokenType::String => format!("'{}'", token.text),
21861                TokenType::QuotedIdentifier => format!("\"{}\"", token.text),
21862                _ => token.text.clone(),
21863            };
21864            tokens.push((text, token.token_type));
21865        }
21866
21867        Ok(Expression::Command(Box::new(Command {
21868            this: self.join_command_tokens(tokens),
21869        })))
21870    }
21871
21872    /// Parse CALL statement (stored procedure call)
21873    /// CALL procedure_name(args, ...)
21874    fn parse_call(&mut self) -> Result<Expression> {
21875        let call_token = self.advance(); // consume CALL
21876
21877        // Collect remaining tokens with their types
21878        let mut tokens = vec![("CALL".to_string(), call_token.token_type)];
21879        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21880            let token = self.advance();
21881            tokens.push((token.text.clone(), token.token_type));
21882        }
21883
21884        Ok(Expression::Command(Box::new(Command {
21885            this: self.join_command_tokens(tokens),
21886        })))
21887    }
21888
21889    /// Parse KILL statement (MySQL/MariaDB)
21890    /// KILL [CONNECTION | QUERY] <id>
21891    fn parse_kill(&mut self) -> Result<Expression> {
21892        self.expect(TokenType::Kill)?;
21893
21894        // Check for optional kind: CONNECTION or QUERY
21895        let kind = if self.match_identifier("CONNECTION") {
21896            Some("CONNECTION".to_string())
21897        } else if self.match_identifier("QUERY") {
21898            Some("QUERY".to_string())
21899        } else {
21900            None
21901        };
21902
21903        // Parse the target (process ID - usually a number or string)
21904        let this = self.parse_primary()?;
21905
21906        Ok(Expression::Kill(Box::new(Kill { this, kind })))
21907    }
21908
21909    /// Parse EXEC/EXECUTE statement (TSQL stored procedure call)
21910    /// EXEC [schema.]procedure_name [@param=value, ...]
21911    fn parse_execute(&mut self) -> Result<Expression> {
21912        self.expect(TokenType::Execute)?;
21913
21914        // Dynamic SQL: EXEC(@sql) or EXEC (@sql)
21915        let this = if self.check(TokenType::LParen) {
21916            self.skip(); // consume (
21917            let expr = self
21918                .parse_disjunction()?
21919                .unwrap_or(Expression::Null(crate::expressions::Null));
21920            self.expect(TokenType::RParen)?;
21921            Expression::Paren(Box::new(crate::expressions::Paren {
21922                this: expr,
21923                trailing_comments: Vec::new(),
21924            }))
21925        } else {
21926            // Parse procedure name (can be qualified: schema.proc_name)
21927            let proc_name = self.parse_table_ref()?;
21928            Expression::Table(Box::new(proc_name))
21929        };
21930
21931        // Parse optional parameters: @param=value [OUTPUT], ...
21932        let mut parameters = Vec::new();
21933
21934        // Check if there are parameters (starts with @ or identifier)
21935        while self.check(TokenType::Var) || self.check(TokenType::Parameter) {
21936            // Get the parameter name (starts with @)
21937            let token = self.advance();
21938            let param_name = if token.text.starts_with('@') {
21939                token.text.clone()
21940            } else {
21941                format!("@{}", token.text)
21942            };
21943
21944            // Check for = (named parameter) or positional parameter
21945            if self.match_token(TokenType::Eq) {
21946                // Named parameter: @param = value
21947                let value = self.parse_primary()?;
21948                let output = self.match_token(TokenType::Output);
21949                parameters.push(ExecuteParameter {
21950                    name: param_name,
21951                    value,
21952                    positional: false,
21953                    output,
21954                });
21955            } else {
21956                // Positional parameter: @var (no = sign)
21957                let output = self.match_token(TokenType::Output);
21958                parameters.push(ExecuteParameter {
21959                    name: param_name.clone(),
21960                    value: Expression::boxed_column(Column {
21961                        name: Identifier::new(&param_name),
21962                        table: None,
21963                        join_mark: false,
21964                        trailing_comments: Vec::new(),
21965                        span: None,
21966                        inferred_type: None,
21967                    }),
21968                    positional: true,
21969                    output,
21970                });
21971            }
21972
21973            // Check for comma to continue
21974            if !self.match_token(TokenType::Comma) {
21975                break;
21976            }
21977        }
21978
21979        // TSQL: WITH RESULT SETS ((...), ...) or WITH RECOMPILE etc.
21980        let suffix = if self.check(TokenType::With) {
21981            let start = self.current;
21982            // Collect remaining tokens until semicolon or end
21983            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
21984                self.skip();
21985            }
21986            Some(self.tokens_to_sql(start, self.current))
21987        } else {
21988            None
21989        };
21990
21991        Ok(Expression::Execute(Box::new(ExecuteStatement {
21992            this,
21993            parameters,
21994            suffix,
21995        })))
21996    }
21997
21998    /// Parse GRANT statement
21999    /// GRANT <privileges> ON [<kind>] <object> TO <principals> [WITH GRANT OPTION]
22000    fn parse_grant(&mut self) -> Result<Expression> {
22001        self.expect(TokenType::Grant)?;
22002
22003        // ClickHouse: GRANT can grant roles (no ON clause), grant privileges (has ON clause),
22004        // or use complex syntax. If we see TO before ON, treat as command.
22005        // Also: multi-privilege grants (multiple ON), wildcard grants (test*.*),
22006        // WITH REPLACE OPTION all parse as commands.
22007        if matches!(
22008            self.config.dialect,
22009            Some(crate::dialects::DialectType::ClickHouse)
22010        ) {
22011            // Save position after GRANT keyword
22012            let saved_pos = self.current;
22013            // Scan ahead to check grant structure
22014            let mut depth = 0i32;
22015            let mut on_count = 0;
22016            let mut found_to = false;
22017            let mut has_star_in_name = false;
22018            let mut has_replace_option = false;
22019            let mut i = self.current;
22020            while i < self.tokens.len() && self.tokens[i].token_type != TokenType::Semicolon {
22021                match self.tokens[i].token_type {
22022                    TokenType::LParen => depth += 1,
22023                    TokenType::RParen => depth -= 1,
22024                    TokenType::On if depth == 0 => on_count += 1,
22025                    TokenType::To if depth == 0 => {
22026                        found_to = true;
22027                    }
22028                    TokenType::Star if depth == 0 && on_count > 0 && !found_to => {
22029                        // Check if star is part of a wildcard name (e.g., test*.*)
22030                        if i > 0
22031                            && self.tokens[i - 1].token_type != TokenType::Dot
22032                            && self.tokens[i - 1].token_type != TokenType::On
22033                        {
22034                            has_star_in_name = true;
22035                        }
22036                    }
22037                    TokenType::Replace if depth == 0 && found_to => {
22038                        has_replace_option = true;
22039                    }
22040                    _ => {}
22041                }
22042                i += 1;
22043            }
22044            if (found_to && on_count == 0) || on_count > 1 || has_star_in_name || has_replace_option
22045            {
22046                // Role grant, multi-privilege grant, wildcard grant, or REPLACE OPTION — parse as command
22047                self.current = saved_pos;
22048                return self
22049                    .parse_command()?
22050                    .ok_or_else(|| self.parse_error("Failed to parse GRANT statement"));
22051            }
22052            self.current = saved_pos;
22053        }
22054
22055        // Parse privileges (e.g., SELECT, INSERT, UPDATE)
22056        let privileges = self.parse_privileges()?;
22057
22058        // Expect ON
22059        self.expect(TokenType::On)?;
22060
22061        // Parse optional kind (TABLE, SCHEMA, FUNCTION, etc.)
22062        let kind = self.parse_object_kind()?;
22063
22064        // Parse securable (the object) - may be dot-separated qualified name
22065        let securable = self.parse_securable_name()?;
22066
22067        // Parse optional function parameter types: func(type1, type2, ...)
22068        let function_params = if self.check(TokenType::LParen) {
22069            self.parse_function_param_types()?
22070        } else {
22071            Vec::new()
22072        };
22073
22074        // Expect TO
22075        self.expect(TokenType::To)?;
22076
22077        // Parse principals
22078        let principals = self.parse_principals()?;
22079
22080        // Check for WITH GRANT OPTION
22081        let grant_option = self.match_token(TokenType::With)
22082            && self.check(TokenType::Grant)
22083            && {
22084                self.skip();
22085                self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTION")
22086            }
22087            && {
22088                self.skip();
22089                true
22090            };
22091
22092        // Check for TSQL AS principal clause
22093        let as_principal = if self.match_token(TokenType::As) {
22094            let name = self.expect_identifier_or_keyword()?;
22095            Some(Identifier::new(name))
22096        } else {
22097            None
22098        };
22099
22100        Ok(Expression::Grant(Box::new(Grant {
22101            privileges,
22102            kind,
22103            securable,
22104            function_params,
22105            principals,
22106            grant_option,
22107            as_principal,
22108        })))
22109    }
22110
22111    /// Parse REVOKE statement
22112    /// REVOKE [GRANT OPTION FOR] <privileges> ON [<kind>] <object> FROM <principals> [CASCADE]
22113    fn parse_revoke(&mut self) -> Result<Expression> {
22114        self.expect(TokenType::Revoke)?;
22115
22116        // ClickHouse: REVOKE role FROM user (no ON clause), multi-privilege, or wildcard — parse as command
22117        if matches!(
22118            self.config.dialect,
22119            Some(crate::dialects::DialectType::ClickHouse)
22120        ) {
22121            let saved_pos = self.current;
22122            let mut depth = 0i32;
22123            let mut on_count = 0;
22124            let mut found_from = false;
22125            let mut has_star_in_name = false;
22126            let mut i = self.current;
22127            while i < self.tokens.len() && self.tokens[i].token_type != TokenType::Semicolon {
22128                match self.tokens[i].token_type {
22129                    TokenType::LParen => depth += 1,
22130                    TokenType::RParen => depth -= 1,
22131                    TokenType::On if depth == 0 => on_count += 1,
22132                    TokenType::From if depth == 0 => {
22133                        found_from = true;
22134                    }
22135                    TokenType::Star if depth == 0 && on_count > 0 && !found_from => {
22136                        if i > 0
22137                            && self.tokens[i - 1].token_type != TokenType::Dot
22138                            && self.tokens[i - 1].token_type != TokenType::On
22139                        {
22140                            has_star_in_name = true;
22141                        }
22142                    }
22143                    _ => {}
22144                }
22145                i += 1;
22146            }
22147            if (found_from && on_count == 0) || on_count > 1 || has_star_in_name {
22148                self.current = saved_pos;
22149                return self
22150                    .parse_command()?
22151                    .ok_or_else(|| self.parse_error("Failed to parse REVOKE statement"));
22152            }
22153            self.current = saved_pos;
22154        }
22155
22156        // Check for GRANT OPTION FOR
22157        let grant_option = if self.check(TokenType::Grant) {
22158            self.skip();
22159            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OPTION") {
22160                self.skip();
22161                self.expect(TokenType::For)?;
22162                true
22163            } else {
22164                return Err(self.parse_error("Expected OPTION after GRANT in REVOKE"));
22165            }
22166        } else {
22167            false
22168        };
22169
22170        // Parse privileges
22171        let privileges = self.parse_privileges()?;
22172
22173        // Expect ON
22174        self.expect(TokenType::On)?;
22175
22176        // Parse optional kind
22177        let kind = self.parse_object_kind()?;
22178
22179        // Parse securable - may be dot-separated qualified name
22180        let securable = self.parse_securable_name()?;
22181
22182        // Parse optional function parameter types: func(type1, type2, ...)
22183        let function_params = if self.check(TokenType::LParen) {
22184            self.parse_function_param_types()?
22185        } else {
22186            Vec::new()
22187        };
22188
22189        // Expect FROM
22190        self.expect(TokenType::From)?;
22191
22192        // Parse principals
22193        let principals = self.parse_principals()?;
22194
22195        // Check for CASCADE or RESTRICT
22196        let cascade = self.match_token(TokenType::Cascade);
22197        let restrict = if !cascade {
22198            self.match_token(TokenType::Restrict)
22199        } else {
22200            false
22201        };
22202
22203        Ok(Expression::Revoke(Box::new(Revoke {
22204            privileges,
22205            kind,
22206            securable,
22207            function_params,
22208            principals,
22209            grant_option,
22210            cascade,
22211            restrict,
22212        })))
22213    }
22214
22215    /// Parse privilege list for GRANT/REVOKE
22216    /// Handles multi-word privileges like "ALL PRIVILEGES" and column-level privileges like "SELECT(col1, col2)"
22217    fn parse_privileges(&mut self) -> Result<Vec<Privilege>> {
22218        let mut privileges = Vec::new();
22219        loop {
22220            let mut priv_parts = Vec::new();
22221            // Collect privilege words until we hit ON, comma, LParen, or similar terminator
22222            while !self.is_at_end() {
22223                if self.check(TokenType::On)
22224                    || self.check(TokenType::Comma)
22225                    || self.check(TokenType::LParen)
22226                {
22227                    break;
22228                }
22229                if self.is_identifier_or_keyword_token() {
22230                    priv_parts.push(self.advance().text.to_ascii_uppercase());
22231                } else {
22232                    break;
22233                }
22234            }
22235            if priv_parts.is_empty() {
22236                break;
22237            }
22238            let priv_name = priv_parts.join(" ");
22239
22240            // Check for column list in parentheses: SELECT(col1, col2)
22241            let columns = if self.match_token(TokenType::LParen) {
22242                let mut cols = Vec::new();
22243                loop {
22244                    // Parse column name (identifier)
22245                    if self.is_identifier_or_keyword_token() {
22246                        cols.push(self.advance().text.to_string());
22247                    } else if self.check(TokenType::RParen) {
22248                        break;
22249                    } else {
22250                        break;
22251                    }
22252                    if !self.match_token(TokenType::Comma) {
22253                        break;
22254                    }
22255                }
22256                self.expect(TokenType::RParen)?;
22257                cols
22258            } else {
22259                Vec::new()
22260            };
22261
22262            privileges.push(Privilege {
22263                name: priv_name,
22264                columns,
22265            });
22266            if !self.match_token(TokenType::Comma) {
22267                break;
22268            }
22269        }
22270        Ok(privileges)
22271    }
22272
22273    /// Parse object kind (TABLE, SCHEMA, FUNCTION, PROCEDURE, SEQUENCE, etc.)
22274    fn parse_object_kind(&mut self) -> Result<Option<String>> {
22275        if self.check(TokenType::Table) {
22276            self.skip();
22277            Ok(Some("TABLE".to_string()))
22278        } else if self.check(TokenType::Schema) {
22279            self.skip();
22280            Ok(Some("SCHEMA".to_string()))
22281        } else if self.check(TokenType::Database) {
22282            self.skip();
22283            Ok(Some("DATABASE".to_string()))
22284        } else if self.check(TokenType::Function) {
22285            self.skip();
22286            Ok(Some("FUNCTION".to_string()))
22287        } else if self.check(TokenType::View) {
22288            self.skip();
22289            Ok(Some("VIEW".to_string()))
22290        } else if self.check(TokenType::Procedure) {
22291            self.skip();
22292            Ok(Some("PROCEDURE".to_string()))
22293        } else if self.check(TokenType::Sequence) {
22294            self.skip();
22295            Ok(Some("SEQUENCE".to_string()))
22296        } else if self.check(TokenType::Warehouse) {
22297            self.skip();
22298            Ok(Some("WAREHOUSE".to_string()))
22299        } else if self.check_identifier("STAGE")
22300            || self.check_identifier("INTEGRATION")
22301            || self.check_identifier("TASK")
22302            || self.check_identifier("STREAM")
22303            || self.check_identifier("PIPE")
22304            || self.check_identifier("TAG")
22305            || self.check_identifier("SHARE")
22306        {
22307            let kind = self.advance().text.to_ascii_uppercase();
22308            Ok(Some(kind))
22309        } else if self.check_identifier("FILE")
22310            && self.current + 1 < self.tokens.len()
22311            && self.tokens[self.current + 1]
22312                .text
22313                .eq_ignore_ascii_case("FORMAT")
22314        {
22315            self.skip(); // consume FILE
22316            self.skip(); // consume FORMAT
22317            Ok(Some("FILE FORMAT".to_string()))
22318        } else if self.check_identifier("NETWORK")
22319            && self.current + 1 < self.tokens.len()
22320            && self.tokens[self.current + 1]
22321                .text
22322                .eq_ignore_ascii_case("POLICY")
22323        {
22324            self.skip(); // consume NETWORK
22325            self.skip(); // consume POLICY
22326            Ok(Some("NETWORK POLICY".to_string()))
22327        } else {
22328            Ok(None)
22329        }
22330    }
22331
22332    /// Parse principal list for GRANT/REVOKE
22333    fn parse_principals(&mut self) -> Result<Vec<GrantPrincipal>> {
22334        let mut principals = Vec::new();
22335        loop {
22336            // Check for ROLE keyword (TokenType::Var with text "ROLE")
22337            let is_role =
22338                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ROLE") {
22339                    self.skip();
22340                    true
22341                } else {
22342                    false
22343                };
22344            // Check for GROUP keyword (Redshift) - TokenType::Group
22345            let is_group = if !is_role && self.check(TokenType::Group) {
22346                self.skip();
22347                true
22348            } else {
22349                false
22350            };
22351            // Check for SHARE keyword (Snowflake)
22352            let is_share = if !is_role && !is_group && self.check_identifier("SHARE") {
22353                self.skip();
22354                true
22355            } else {
22356                false
22357            };
22358            // Parse principal name (with quoted flag preserved for backtick-quoted identifiers)
22359            let name = self.expect_identifier_or_keyword_with_quoted()?;
22360            principals.push(GrantPrincipal {
22361                name,
22362                is_role,
22363                is_group,
22364                is_share,
22365            });
22366            if !self.match_token(TokenType::Comma) {
22367                break;
22368            }
22369        }
22370        Ok(principals)
22371    }
22372
22373    /// Parse a securable name (potentially dot-separated qualified name)
22374    /// e.g., "mydb.myschema.ADD5" -> Identifier("mydb.myschema.ADD5")
22375    fn parse_securable_name(&mut self) -> Result<Identifier> {
22376        // Accept * as a name part (e.g., GRANT ON *.* or GRANT ON db.*)
22377        let first = if self.match_token(TokenType::Star) {
22378            "*".to_string()
22379        } else {
22380            self.expect_identifier_or_keyword()?
22381        };
22382        let mut parts = vec![first];
22383
22384        while self.match_token(TokenType::Dot) {
22385            let next = if self.match_token(TokenType::Star) {
22386                "*".to_string()
22387            } else {
22388                self.expect_identifier_or_keyword()?
22389            };
22390            parts.push(next);
22391        }
22392
22393        Ok(Identifier::new(parts.join(".")))
22394    }
22395
22396    /// Parse function parameter types for GRANT/REVOKE ON FUNCTION
22397    /// e.g., "(number, varchar)" -> vec!["number", "varchar"]
22398    fn parse_function_param_types(&mut self) -> Result<Vec<String>> {
22399        self.expect(TokenType::LParen)?;
22400
22401        let mut params = Vec::new();
22402        if !self.check(TokenType::RParen) {
22403            loop {
22404                // Parse parameter type - can be a keyword (INT, VARCHAR) or identifier
22405                let param_type = self.expect_identifier_or_keyword()?;
22406                params.push(param_type);
22407                if !self.match_token(TokenType::Comma) {
22408                    break;
22409                }
22410            }
22411        }
22412
22413        self.expect(TokenType::RParen)?;
22414        Ok(params)
22415    }
22416
22417    /// Parse COMMENT ON statement
22418    fn parse_comment(&mut self) -> Result<Expression> {
22419        self.expect(TokenType::Comment)?;
22420
22421        // Check for IF EXISTS
22422        let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
22423
22424        // Expect ON
22425        self.expect(TokenType::On)?;
22426
22427        // Check for MATERIALIZED (can be TokenType::Materialized or TokenType::Var)
22428        let materialized = if self.match_token(TokenType::Materialized) {
22429            true
22430        } else if self.check(TokenType::Var)
22431            && self.peek().text.eq_ignore_ascii_case("MATERIALIZED")
22432        {
22433            self.skip();
22434            true
22435        } else {
22436            false
22437        };
22438
22439        // Parse the object kind (COLUMN, TABLE, DATABASE, PROCEDURE, etc.)
22440        let kind = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
22441
22442        // Parse the object name (can be qualified like schema.table.column)
22443        // For PROCEDURE/FUNCTION, we need to handle the parameter list like my_proc(integer, integer)
22444        let this = if kind == "PROCEDURE" || kind == "FUNCTION" {
22445            // Parse name possibly with parameter types, preserving original case
22446            let name_token = self.advance();
22447            let mut name_str = name_token.text.clone();
22448
22449            // Parse additional qualified parts
22450            while self.match_token(TokenType::Dot) {
22451                let next = self.advance();
22452                name_str.push('.');
22453                name_str.push_str(&next.text);
22454            }
22455
22456            // Check for parameter types in parentheses
22457            if self.match_token(TokenType::LParen) {
22458                name_str.push('(');
22459                let mut first = true;
22460                while !self.check(TokenType::RParen) && !self.is_at_end() {
22461                    if !first {
22462                        name_str.push_str(", ");
22463                    }
22464                    first = false;
22465                    let param_token = self.advance();
22466                    name_str.push_str(&param_token.text);
22467                    self.match_token(TokenType::Comma);
22468                }
22469                self.expect(TokenType::RParen)?;
22470                name_str.push(')');
22471            }
22472
22473            Expression::Identifier(Identifier::new(name_str))
22474        } else {
22475            self.parse_qualified_name()?
22476        };
22477
22478        // Expect IS
22479        if self.check(TokenType::Is) {
22480            self.skip();
22481        } else {
22482            return Err(self.parse_error("Expected IS in COMMENT ON statement"));
22483        }
22484
22485        // Parse the comment expression (usually a string literal)
22486        let expression = self.parse_primary()?;
22487
22488        Ok(Expression::Comment(Box::new(Comment {
22489            this,
22490            kind,
22491            expression,
22492            exists,
22493            materialized,
22494        })))
22495    }
22496
22497    /// Parse SET statement
22498    fn parse_set(&mut self) -> Result<Expression> {
22499        self.expect(TokenType::Set)?;
22500
22501        let mut items = Vec::new();
22502
22503        // ClickHouse: SET DEFAULT ROLE ... TO user - parse as command
22504        if matches!(
22505            self.config.dialect,
22506            Some(crate::dialects::DialectType::ClickHouse)
22507        ) && self.check(TokenType::Default)
22508        {
22509            let mut parts = vec!["SET".to_string()];
22510            while !self.is_at_end() && self.peek().token_type != TokenType::Semicolon {
22511                parts.push(self.advance().text.clone());
22512            }
22513            return Ok(Expression::Command(Box::new(crate::expressions::Command {
22514                this: parts.join(" "),
22515            })));
22516        }
22517
22518        // Teradata: SET QUERY_BAND = ... [UPDATE] [FOR scope]
22519        if matches!(
22520            self.config.dialect,
22521            Some(crate::dialects::DialectType::Teradata)
22522        ) && self.match_identifier("QUERY_BAND")
22523        {
22524            return self.parse_query_band();
22525        }
22526
22527        // Handle MySQL SET CHARACTER SET / SET NAMES
22528        if self.match_identifier("CHARACTER") {
22529            // SET CHARACTER SET <charset> | SET CHARACTER SET DEFAULT
22530            self.expect(TokenType::Set)?;
22531            let value = if self.match_token(TokenType::Default) {
22532                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22533            } else {
22534                self.parse_primary()?
22535            };
22536            items.push(SetItem {
22537                name: Expression::Identifier(Identifier::new("CHARACTER SET".to_string())),
22538                value,
22539                kind: None,
22540                no_equals: false,
22541            });
22542            return Ok(Expression::SetStatement(Box::new(SetStatement { items })));
22543        }
22544
22545        if self.match_identifier("NAMES") {
22546            // SET NAMES <charset> [COLLATE <collation>] | SET NAMES DEFAULT
22547            let value = if self.match_token(TokenType::Default) {
22548                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22549            } else {
22550                self.parse_primary()?
22551            };
22552            // Check for optional COLLATE clause
22553            let collation = if self.match_identifier("COLLATE") {
22554                Some(self.parse_primary()?)
22555            } else {
22556                None
22557            };
22558            items.push(SetItem {
22559                name: Expression::Identifier(Identifier::new("NAMES".to_string())),
22560                value,
22561                kind: None,
22562                no_equals: false,
22563            });
22564            if let Some(coll) = collation {
22565                items.push(SetItem {
22566                    name: Expression::Identifier(Identifier::new("COLLATE".to_string())),
22567                    value: coll,
22568                    kind: None,
22569                    no_equals: false,
22570                });
22571            }
22572            return Ok(Expression::SetStatement(Box::new(SetStatement { items })));
22573        }
22574
22575        // Track whether SET VAR/VARIABLE was used (only first item gets the VARIABLE kind)
22576        let mut set_is_variable = if self.check(TokenType::Var) {
22577            let text = self.peek().text.to_uppercase();
22578            if text == "VARIABLE" || text == "VAR" {
22579                // Look ahead: VAR/VARIABLE should be followed by another name, not by = or TO
22580                if let Some(next) = self.tokens.get(self.current + 1) {
22581                    if next.token_type != TokenType::Eq
22582                        && next.token_type != TokenType::To
22583                        && next.token_type != TokenType::ColonEq
22584                    {
22585                        self.skip(); // consume VAR/VARIABLE
22586                        true
22587                    } else {
22588                        false
22589                    }
22590                } else {
22591                    false
22592                }
22593            } else {
22594                false
22595            }
22596        } else {
22597            false
22598        };
22599
22600        loop {
22601            // Check for GLOBAL, LOCAL, SESSION, PERSIST, PERSIST_ONLY modifiers
22602            // LOCAL is a token type, others are identifiers
22603            let kind = if self.match_identifier("GLOBAL") {
22604                Some("GLOBAL".to_string())
22605            } else if self.match_token(TokenType::Local) {
22606                Some("LOCAL".to_string())
22607            } else if self.match_identifier("SESSION") {
22608                Some("SESSION".to_string())
22609            } else if self.match_identifier("PERSIST") {
22610                Some("PERSIST".to_string())
22611            } else if self.match_identifier("PERSIST_ONLY") {
22612                Some("PERSIST_ONLY".to_string())
22613            } else if set_is_variable {
22614                set_is_variable = false; // Only first item gets VARIABLE kind
22615                Some("VARIABLE".to_string())
22616            } else {
22617                None
22618            };
22619
22620            // Check for SET [GLOBAL|SESSION] TRANSACTION (MySQL)
22621            if self.match_token(TokenType::Transaction) {
22622                // Parse transaction characteristics (ISOLATION LEVEL, READ ONLY, READ WRITE)
22623                let mut characteristics = Vec::new();
22624                loop {
22625                    let mut char_tokens = Vec::new();
22626                    // Parse ISOLATION LEVEL ... or READ ONLY/WRITE
22627                    // Must handle keywords like ONLY, REPEATABLE, SERIALIZABLE, etc.
22628                    while !self.is_at_end()
22629                        && !self.check(TokenType::Comma)
22630                        && !self.check(TokenType::Semicolon)
22631                    {
22632                        // Allow identifiers and common transaction-related keywords
22633                        if self.is_identifier_token()
22634                            || self.is_safe_keyword_as_identifier()
22635                            || self.check(TokenType::Only)
22636                            || self.check(TokenType::Repeatable)
22637                        {
22638                            char_tokens.push(self.advance().text);
22639                        } else {
22640                            break;
22641                        }
22642                    }
22643                    if !char_tokens.is_empty() {
22644                        characteristics.push(char_tokens.join(" "));
22645                    }
22646                    if !self.match_token(TokenType::Comma) {
22647                        break;
22648                    }
22649                }
22650
22651                let name = Expression::Identifier(Identifier::new("TRANSACTION".to_string()));
22652                let value = if characteristics.is_empty() {
22653                    Expression::Identifier(Identifier::new("".to_string()))
22654                } else {
22655                    Expression::Identifier(Identifier::new(characteristics.join(", ")))
22656                };
22657
22658                items.push(SetItem {
22659                    name,
22660                    value,
22661                    kind,
22662                    no_equals: false,
22663                });
22664                break;
22665            }
22666
22667            // Parse variable name - use a simple approach to avoid expression parsing issues
22668            // Variable names can be dotted identifiers or keywords used as names
22669            let name = {
22670                if self.check(TokenType::AtAt) {
22671                    // @@SCOPE.variable or @@variable syntax (MySQL system variables)
22672                    self.skip(); // consume @@
22673                    let mut name_str = "@@".to_string();
22674                    let first = self.advance().text.clone();
22675                    name_str.push_str(&first);
22676                    // Handle @@scope.variable (e.g., @@GLOBAL.max_connections)
22677                    while self.match_token(TokenType::Dot) {
22678                        let next = self.advance().text.clone();
22679                        name_str.push('.');
22680                        name_str.push_str(&next);
22681                    }
22682                    Expression::Identifier(Identifier::new(name_str))
22683                } else if self.check(TokenType::DAt) {
22684                    // @variable syntax (MySQL user variables)
22685                    self.skip(); // consume @
22686                    let mut name_str = "@".to_string();
22687                    let first = self.advance().text.clone();
22688                    name_str.push_str(&first);
22689                    Expression::Identifier(Identifier::new(name_str))
22690                } else if self.check(TokenType::LParen) {
22691                    // Tuple of variable names: SET VARIABLE (v1, v2) = (SELECT ...)
22692                    self.skip(); // consume (
22693                    let mut vars = Vec::new();
22694                    loop {
22695                        let var_name = self.advance().text.clone();
22696                        vars.push(Expression::Column(Box::new(Column {
22697                            name: Identifier::new(var_name),
22698                            table: None,
22699                            join_mark: false,
22700                            trailing_comments: Vec::new(),
22701                            span: None,
22702                            inferred_type: None,
22703                        })));
22704                        if !self.match_token(TokenType::Comma) {
22705                            break;
22706                        }
22707                    }
22708                    self.expect(TokenType::RParen)?;
22709                    Expression::Tuple(Box::new(crate::expressions::Tuple { expressions: vars }))
22710                } else {
22711                    let first = self.advance().text.clone();
22712                    let mut name_str = first;
22713                    // Handle dotted identifiers (e.g., schema.variable)
22714                    while self.match_token(TokenType::Dot) {
22715                        let next = self.advance().text.clone();
22716                        name_str.push('.');
22717                        name_str.push_str(&next);
22718                    }
22719                    // Handle Hive-style colon-separated names (e.g., hiveconf:some_var)
22720                    // But not := which is assignment
22721                    while self.check(TokenType::Colon) && !self.check_next(TokenType::Eq) {
22722                        self.skip(); // consume :
22723                        let next = self.advance().text.clone();
22724                        name_str.push(':');
22725                        name_str.push_str(&next);
22726                    }
22727                    Expression::Identifier(Identifier::new(name_str))
22728                }
22729            };
22730
22731            // Expect = or := or TO
22732            if self.match_token(TokenType::Eq) || self.match_token(TokenType::ColonEq) {
22733                // ok - standard assignment
22734            } else if self.match_token(TokenType::To) {
22735                // PostgreSQL uses SET var TO value
22736            } else if self.is_at_end()
22737                || self.check(TokenType::Semicolon)
22738                || self.check(TokenType::Comma)
22739            {
22740                // SET x ON/OFF without = (TSQL: SET XACT_ABORT ON)
22741                // The ON/OFF was already parsed as part of the name expression
22742                // Handle as a name-only set (value is empty)
22743                items.push(SetItem {
22744                    name,
22745                    value: Expression::Identifier(Identifier::new("".to_string())),
22746                    kind,
22747                    no_equals: false,
22748                });
22749                if !self.match_token(TokenType::Comma) {
22750                    break;
22751                }
22752                continue;
22753            } else {
22754                // Check if the next token looks like a value (ON/OFF without =)
22755                // TSQL: SET XACT_ABORT ON, SET NOCOUNT ON
22756                if self.check(TokenType::On) || self.check_keyword_text("OFF") {
22757                    let val = self.advance().text;
22758                    // Include ON/OFF in the name so generator doesn't add "="
22759                    let name_with_val = match &name {
22760                        Expression::Column(col) => format!("{} {}", col.name.name, val),
22761                        Expression::Identifier(id) => format!("{} {}", id.name, val),
22762                        _ => val.clone(),
22763                    };
22764                    items.push(SetItem {
22765                        name: Expression::Identifier(Identifier::new(name_with_val)),
22766                        value: Expression::Identifier(Identifier::new("".to_string())),
22767                        kind,
22768                        no_equals: false,
22769                    });
22770                    if !self.match_token(TokenType::Comma) {
22771                        break;
22772                    }
22773                    continue;
22774                }
22775                // TSQL/Generic: SET key value (without = or TO)
22776                // Parse the next token as the value
22777                if !self.is_at_end() && !self.check(TokenType::Semicolon) {
22778                    let value = self.parse_expression()?;
22779                    items.push(SetItem {
22780                        name,
22781                        value,
22782                        kind,
22783                        no_equals: true,
22784                    });
22785                    if !self.match_token(TokenType::Comma) {
22786                        break;
22787                    }
22788                    continue;
22789                }
22790                return Err(self.parse_error("Expected '=' or 'TO' in SET statement"));
22791            }
22792
22793            // Parse value - handle ON/OFF keywords as identifiers (MySQL: SET autocommit = ON)
22794            let value = if self.check(TokenType::On) || self.check_keyword_text("OFF") {
22795                Expression::Identifier(Identifier::new(self.advance().text.clone()))
22796            } else if self.match_token(TokenType::Default) {
22797                Expression::Identifier(Identifier::new("DEFAULT".to_string()))
22798            } else {
22799                self.parse_expression()?
22800            };
22801
22802            items.push(SetItem {
22803                name,
22804                value,
22805                kind,
22806                no_equals: false,
22807            });
22808
22809            if !self.match_token(TokenType::Comma) {
22810                break;
22811            }
22812        }
22813
22814        Ok(Expression::SetStatement(Box::new(SetStatement { items })))
22815    }
22816
22817    /// Parse Teradata SET QUERY_BAND statement
22818    fn parse_query_band(&mut self) -> Result<Expression> {
22819        self.expect(TokenType::Eq)?;
22820
22821        let value = if self.match_identifier("NONE") {
22822            Expression::Var(Box::new(Var {
22823                this: "NONE".to_string(),
22824            }))
22825        } else if self.check(TokenType::String) {
22826            Expression::Literal(Box::new(Literal::String(self.expect_string()?)))
22827        } else {
22828            self.parse_primary()?
22829        };
22830
22831        let update = if self.match_token(TokenType::Update) || self.match_identifier("UPDATE") {
22832            Some(Box::new(Expression::Boolean(BooleanLiteral {
22833                value: true,
22834            })))
22835        } else {
22836            None
22837        };
22838
22839        let _ = self.match_token(TokenType::For);
22840
22841        let scope = if self.match_token(TokenType::Session) || self.match_identifier("SESSION") {
22842            if self.match_identifier("VOLATILE") {
22843                Some("SESSION VOLATILE".to_string())
22844            } else {
22845                Some("SESSION".to_string())
22846            }
22847        } else if self.match_token(TokenType::Transaction) || self.match_identifier("TRANSACTION") {
22848            Some("TRANSACTION".to_string())
22849        } else if self.match_identifier("VOLATILE") {
22850            Some("VOLATILE".to_string())
22851        } else {
22852            None
22853        };
22854
22855        Ok(Expression::QueryBand(Box::new(QueryBand {
22856            this: Box::new(value),
22857            scope: scope.map(|s| Box::new(Expression::Var(Box::new(Var { this: s })))),
22858            update,
22859        })))
22860    }
22861
22862    /// Parse FETCH FIRST/NEXT clause
22863    fn parse_fetch(&mut self) -> Result<Fetch> {
22864        // FETCH [FIRST|NEXT] [count] [PERCENT] [ROW|ROWS] [ONLY|WITH TIES]
22865
22866        // FIRST or NEXT
22867        let direction = if self.match_token(TokenType::First) {
22868            "FIRST".to_string()
22869        } else if self.match_token(TokenType::Next) {
22870            "NEXT".to_string()
22871        } else {
22872            "FIRST".to_string() // Default
22873        };
22874
22875        // Optional count - but check if next token is ROW/ROWS/PERCENT/ONLY (no count)
22876        let count = if !self.check(TokenType::Row)
22877            && !self.check(TokenType::Rows)
22878            && !self.check(TokenType::Percent)
22879            && !self.check(TokenType::Only)
22880        {
22881            // Accept number, parenthesized expression, or TSQL @variable (Var token)
22882            if self.check(TokenType::Number)
22883                || self.check(TokenType::LParen)
22884                || self.check(TokenType::DAt)
22885                || self.check(TokenType::Var)
22886            {
22887                Some(self.parse_primary()?)
22888            } else {
22889                None
22890            }
22891        } else {
22892            None
22893        };
22894
22895        // PERCENT modifier
22896        let percent = self.match_token(TokenType::Percent);
22897
22898        // ROW or ROWS
22899        let rows = self.match_token(TokenType::Row) || self.match_token(TokenType::Rows);
22900
22901        // ONLY or WITH TIES
22902        self.match_token(TokenType::Only);
22903        let with_ties = self.match_keywords(&[TokenType::With, TokenType::Ties]);
22904
22905        Ok(Fetch {
22906            direction,
22907            count,
22908            percent,
22909            rows,
22910            with_ties,
22911        })
22912    }
22913
22914    /// Parse a qualified name (schema.table.column or just table)
22915    fn parse_qualified_name(&mut self) -> Result<Expression> {
22916        let first = self.expect_identifier_or_keyword()?;
22917        let mut parts = vec![first];
22918
22919        while self.match_token(TokenType::Dot) {
22920            let next = self.expect_identifier_or_keyword()?;
22921            parts.push(next);
22922        }
22923
22924        if parts.len() == 1 {
22925            Ok(Expression::Identifier(Identifier::new(parts.remove(0))))
22926        } else if parts.len() == 2 {
22927            Ok(Expression::boxed_column(Column {
22928                table: Some(Identifier::new(parts[0].clone())),
22929                name: Identifier::new(parts[1].clone()),
22930                join_mark: false,
22931                trailing_comments: Vec::new(),
22932                span: None,
22933                inferred_type: None,
22934            }))
22935        } else {
22936            // For 3+ parts, create a Column with concatenated table parts
22937            let column_name = parts.pop().unwrap();
22938            let table_name = parts.join(".");
22939            Ok(Expression::boxed_column(Column {
22940                table: Some(Identifier::new(table_name)),
22941                name: Identifier::new(column_name),
22942                join_mark: false,
22943                trailing_comments: Vec::new(),
22944                span: None,
22945                inferred_type: None,
22946            }))
22947        }
22948    }
22949
22950    // ==================== Phase 4: Additional DDL Parsing ====================
22951
22952    /// Parse CREATE SCHEMA statement
22953    fn parse_create_schema(&mut self, leading_comments: Vec<String>) -> Result<Expression> {
22954        self.expect(TokenType::Schema)?;
22955
22956        let if_not_exists =
22957            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
22958        let name = self.parse_identifier_parts()?;
22959
22960        // Parse CLONE clause (Snowflake)
22961        let clone_from = if self.match_identifier("CLONE") {
22962            Some(self.parse_identifier_parts()?)
22963        } else {
22964            None
22965        };
22966
22967        // Parse AT/BEFORE clause for time travel (Snowflake)
22968        // Note: BEFORE is a keyword token, AT is an identifier
22969        let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
22970            let keyword = self.previous().text.to_ascii_uppercase();
22971            self.expect(TokenType::LParen)?;
22972            // Parse the content: OFFSET => value or TIMESTAMP => value
22973            let mut result = format!("{} (", keyword);
22974            let mut prev_token_type: Option<TokenType> = None;
22975            let mut paren_depth = 1; // Track nested parens
22976            while !self.is_at_end() && paren_depth > 0 {
22977                let token = self.advance();
22978                if token.token_type == TokenType::LParen {
22979                    paren_depth += 1;
22980                } else if token.token_type == TokenType::RParen {
22981                    paren_depth -= 1;
22982                    if paren_depth == 0 {
22983                        break; // Don't include the closing paren in result yet
22984                    }
22985                }
22986                // Smart spacing: no space after ( or => or - and no space before (
22987                let needs_space = !result.ends_with('(')
22988                    && prev_token_type != Some(TokenType::Arrow)
22989                    && prev_token_type != Some(TokenType::Dash)
22990                    && prev_token_type != Some(TokenType::LParen)
22991                    && token.token_type != TokenType::LParen; // no space before (
22992                if needs_space
22993                    && token.token_type != TokenType::RParen
22994                    && token.token_type != TokenType::Comma
22995                {
22996                    result.push(' ');
22997                }
22998                // Properly quote string literals
22999                if token.token_type == TokenType::String {
23000                    result.push('\'');
23001                    result.push_str(&token.text.replace('\'', "''"));
23002                    result.push('\'');
23003                } else {
23004                    result.push_str(&token.text);
23005                }
23006                if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma {
23007                    result.push(' ');
23008                }
23009                prev_token_type = Some(token.token_type);
23010            }
23011            result.push(')');
23012            Some(Expression::Raw(Raw { sql: result }))
23013        } else {
23014            None
23015        };
23016
23017        let authorization = if self.match_token(TokenType::Authorization) {
23018            Some(Identifier::new(self.expect_identifier()?))
23019        } else {
23020            None
23021        };
23022
23023        // Parse schema properties like DEFAULT COLLATE or WITH (properties)
23024        let mut properties = Vec::new();
23025
23026        // Parse WITH (prop1=val1, prop2=val2, ...) (Trino/Presto)
23027        if self.match_token(TokenType::With) {
23028            self.expect(TokenType::LParen)?;
23029            loop {
23030                // Parse property name (identifier or string)
23031                let prop_name = if self.check(TokenType::String) {
23032                    Expression::Literal(Box::new(Literal::String(self.expect_string()?)))
23033                } else {
23034                    Expression::Identifier(Identifier::new(self.expect_identifier_or_keyword()?))
23035                };
23036                self.expect(TokenType::Eq)?;
23037                // Parse property value
23038                let prop_value = self.parse_expression()?;
23039                // Create Property expression: key=value
23040                properties.push(Expression::Property(Box::new(Property {
23041                    this: Box::new(prop_name),
23042                    value: Some(Box::new(prop_value)),
23043                })));
23044                if !self.match_token(TokenType::Comma) {
23045                    break;
23046                }
23047            }
23048            self.expect(TokenType::RParen)?;
23049        }
23050
23051        // Parse DEFAULT COLLATE 'value' (BigQuery)
23052        if self.match_token(TokenType::Default) && self.match_token(TokenType::Collate) {
23053            // Parse the collation value (could be string literal or identifier)
23054            let collation = self.parse_primary()?;
23055            properties.push(Expression::CollateProperty(Box::new(CollateProperty {
23056                this: Box::new(collation),
23057                default: Some(Box::new(Expression::Boolean(BooleanLiteral {
23058                    value: true,
23059                }))),
23060            })));
23061        }
23062
23063        Ok(Expression::CreateSchema(Box::new(CreateSchema {
23064            name,
23065            if_not_exists,
23066            authorization,
23067            clone_from,
23068            at_clause,
23069            properties,
23070            leading_comments,
23071        })))
23072    }
23073
23074    /// Parse DROP SCHEMA statement
23075    fn parse_drop_schema(&mut self) -> Result<Expression> {
23076        self.expect(TokenType::Schema)?;
23077
23078        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23079        let name = Identifier::new(self.expect_identifier()?);
23080
23081        let cascade = self.match_token(TokenType::Cascade);
23082        if !cascade {
23083            self.match_token(TokenType::Restrict);
23084        }
23085
23086        Ok(Expression::DropSchema(Box::new(DropSchema {
23087            name,
23088            if_exists,
23089            cascade,
23090        })))
23091    }
23092
23093    /// Parse CREATE DATABASE statement
23094    fn parse_create_database(&mut self) -> Result<Expression> {
23095        self.expect(TokenType::Database)?;
23096
23097        let if_not_exists =
23098            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
23099        let name = Identifier::new(self.expect_identifier()?);
23100
23101        // Check for Snowflake CLONE clause
23102        let clone_from = if self.match_identifier("CLONE") {
23103            Some(Identifier::new(self.expect_identifier()?))
23104        } else {
23105            None
23106        };
23107
23108        // Parse AT/BEFORE clause for time travel (Snowflake)
23109        // Note: BEFORE is a keyword token, AT is an identifier
23110        let at_clause = if self.match_identifier("AT") || self.match_token(TokenType::Before) {
23111            let keyword = self.previous().text.to_ascii_uppercase();
23112            self.expect(TokenType::LParen)?;
23113            // Parse the content: OFFSET => value or TIMESTAMP => value
23114            let mut result = format!("{} (", keyword);
23115            let mut prev_token_type: Option<TokenType> = None;
23116            let mut paren_depth = 1; // Track nested parens
23117            while !self.is_at_end() && paren_depth > 0 {
23118                let token = self.advance();
23119                if token.token_type == TokenType::LParen {
23120                    paren_depth += 1;
23121                } else if token.token_type == TokenType::RParen {
23122                    paren_depth -= 1;
23123                    if paren_depth == 0 {
23124                        break; // Don't include the closing paren in result yet
23125                    }
23126                }
23127                // Smart spacing: no space after ( or => or - and no space before (
23128                let needs_space = !result.ends_with('(')
23129                    && prev_token_type != Some(TokenType::Arrow)
23130                    && prev_token_type != Some(TokenType::Dash)
23131                    && prev_token_type != Some(TokenType::LParen)
23132                    && token.token_type != TokenType::LParen; // no space before (
23133                if needs_space
23134                    && token.token_type != TokenType::RParen
23135                    && token.token_type != TokenType::Comma
23136                {
23137                    result.push(' ');
23138                }
23139                // Properly quote string literals
23140                if token.token_type == TokenType::String {
23141                    result.push('\'');
23142                    result.push_str(&token.text.replace('\'', "''"));
23143                    result.push('\'');
23144                } else {
23145                    result.push_str(&token.text);
23146                }
23147                if token.token_type == TokenType::Arrow || token.token_type == TokenType::Comma {
23148                    result.push(' ');
23149                }
23150                prev_token_type = Some(token.token_type);
23151            }
23152            result.push(')');
23153            Some(Expression::Raw(Raw { sql: result }))
23154        } else {
23155            None
23156        };
23157
23158        // ClickHouse: ON CLUSTER clause
23159        let _on_cluster = self.parse_on_cluster_clause()?;
23160
23161        let mut options = Vec::new();
23162
23163        // Parse database options
23164        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
23165            if self.match_identifier("OWNER") || self.match_token(TokenType::Eq) {
23166                self.match_token(TokenType::Eq);
23167                options.push(DatabaseOption::Owner(Identifier::new(
23168                    self.expect_identifier()?,
23169                )));
23170            } else if self.match_identifier("TEMPLATE") {
23171                self.match_token(TokenType::Eq);
23172                options.push(DatabaseOption::Template(Identifier::new(
23173                    self.expect_identifier()?,
23174                )));
23175            } else if self.match_identifier("ENCODING") {
23176                self.match_token(TokenType::Eq);
23177                let encoding = if self.check(TokenType::String) {
23178                    let tok = self.advance();
23179                    tok.text.trim_matches('\'').to_string()
23180                } else {
23181                    self.expect_identifier()?
23182                };
23183                options.push(DatabaseOption::Encoding(encoding));
23184            } else if self.match_identifier("CHARACTER") {
23185                self.match_token(TokenType::Set);
23186                self.match_token(TokenType::Eq);
23187                let charset = if self.check(TokenType::String) {
23188                    let tok = self.advance();
23189                    tok.text.trim_matches('\'').to_string()
23190                } else {
23191                    self.expect_identifier()?
23192                };
23193                options.push(DatabaseOption::CharacterSet(charset));
23194            } else if self.match_identifier("COLLATE") {
23195                self.match_token(TokenType::Eq);
23196                let collate = if self.check(TokenType::String) {
23197                    let tok = self.advance();
23198                    tok.text.trim_matches('\'').to_string()
23199                } else {
23200                    self.expect_identifier()?
23201                };
23202                options.push(DatabaseOption::Collate(collate));
23203            } else if self.match_identifier("LOCATION") {
23204                self.match_token(TokenType::Eq);
23205                let loc = if self.check(TokenType::String) {
23206                    let tok = self.advance();
23207                    tok.text.trim_matches('\'').to_string()
23208                } else {
23209                    self.expect_identifier()?
23210                };
23211                options.push(DatabaseOption::Location(loc));
23212            } else {
23213                break;
23214            }
23215        }
23216
23217        Ok(Expression::CreateDatabase(Box::new(CreateDatabase {
23218            name,
23219            if_not_exists,
23220            options,
23221            clone_from,
23222            at_clause,
23223        })))
23224    }
23225
23226    /// Parse DROP DATABASE statement
23227    fn parse_drop_database(&mut self) -> Result<Expression> {
23228        self.expect(TokenType::Database)?;
23229
23230        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23231
23232        // ClickHouse: IF EMPTY
23233        if !if_exists
23234            && matches!(
23235                self.config.dialect,
23236                Some(crate::dialects::DialectType::ClickHouse)
23237            )
23238        {
23239            if self.check(TokenType::If)
23240                && self.current + 1 < self.tokens.len()
23241                && self.tokens[self.current + 1]
23242                    .text
23243                    .eq_ignore_ascii_case("EMPTY")
23244            {
23245                self.skip(); // consume IF
23246                self.skip(); // consume EMPTY
23247            }
23248        }
23249        let name = Identifier::new(self.expect_identifier()?);
23250
23251        // ClickHouse: ON CLUSTER clause
23252        let sync = if matches!(
23253            self.config.dialect,
23254            Some(crate::dialects::DialectType::ClickHouse)
23255        ) {
23256            let _ = self.parse_on_cluster_clause()?;
23257            self.match_identifier("SYNC")
23258        } else {
23259            false
23260        };
23261
23262        Ok(Expression::DropDatabase(Box::new(DropDatabase {
23263            name,
23264            if_exists,
23265            sync,
23266        })))
23267    }
23268
23269    /// Parse CREATE FUNCTION statement
23270    fn parse_create_function(
23271        &mut self,
23272        or_replace: bool,
23273        or_alter: bool,
23274        temporary: bool,
23275        is_table_function: bool,
23276    ) -> Result<Expression> {
23277        self.expect(TokenType::Function)?;
23278
23279        let if_not_exists =
23280            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
23281        let name = self.parse_table_ref()?;
23282
23283        // Parse parameters (optional - some dialects allow CREATE FUNCTION f AS 'body')
23284        let (parameters, has_parens) = if self.match_token(TokenType::LParen) {
23285            let params = self.parse_function_parameters()?;
23286            self.expect(TokenType::RParen)?;
23287            (params, true)
23288        } else {
23289            (Vec::new(), false)
23290        };
23291
23292        // Track if LANGUAGE appears before RETURNS
23293        let mut language_first = false;
23294        let mut return_type = None;
23295        let mut language = None;
23296        let mut sql_data_access = None;
23297
23298        // Check for LANGUAGE before RETURNS
23299        if self.match_token(TokenType::Language) {
23300            language = Some(self.expect_identifier_or_keyword()?);
23301            language_first = true;
23302        }
23303
23304        // Parse RETURNS clause (may come before or after LANGUAGE)
23305        let mut returns_table_body: Option<String> = None;
23306        if self.match_token(TokenType::Returns) {
23307            if self.check(TokenType::Var) && self.peek().text.starts_with('@') {
23308                // TSQL: RETURNS @var TABLE (col_defs)
23309                let var_name = self.advance().text.clone();
23310                if self.check(TokenType::Table) {
23311                    self.skip(); // consume TABLE
23312                    return_type = Some(DataType::Custom {
23313                        name: "TABLE".to_string(),
23314                    });
23315                    // Parse column definitions
23316                    if self.match_token(TokenType::LParen) {
23317                        let start = self.current;
23318                        let mut depth = 1;
23319                        while depth > 0 && !self.is_at_end() {
23320                            if self.check(TokenType::LParen) {
23321                                depth += 1;
23322                            }
23323                            if self.check(TokenType::RParen) {
23324                                depth -= 1;
23325                                if depth == 0 {
23326                                    break;
23327                                }
23328                            }
23329                            self.skip();
23330                        }
23331                        // Reconstruct the column definitions with proper spacing
23332                        let mut col_defs_str = String::new();
23333                        for (i, tok) in self.tokens[start..self.current].iter().enumerate() {
23334                            // Don't add space before comma, LParen, RParen
23335                            // Don't add space after LParen
23336                            let prev_tok = if i > 0 {
23337                                Some(&self.tokens[start + i - 1])
23338                            } else {
23339                                None
23340                            };
23341                            let needs_space = i > 0
23342                                && tok.token_type != TokenType::Comma
23343                                && tok.token_type != TokenType::RParen
23344                                && tok.token_type != TokenType::LParen
23345                                && prev_tok
23346                                    .map(|p| p.token_type != TokenType::LParen)
23347                                    .unwrap_or(true);
23348                            if needs_space {
23349                                col_defs_str.push(' ');
23350                            }
23351                            col_defs_str.push_str(&tok.text);
23352                        }
23353                        returns_table_body = Some(format!("{} TABLE ({})", var_name, col_defs_str));
23354                        self.expect(TokenType::RParen)?;
23355                    } else {
23356                        returns_table_body = Some(format!("{} TABLE", var_name));
23357                    }
23358                } else {
23359                    // Parse data type after var name
23360                    return_type = Some(self.parse_data_type()?);
23361                }
23362            } else if self.check(TokenType::Table) {
23363                // Could be:
23364                // - TSQL: RETURNS TABLE AS RETURN ...
23365                // - BigQuery: RETURNS TABLE <col1 TYPE, col2 TYPE>
23366                // - Snowflake: RETURNS TABLE(col1 TYPE, col2 TYPE)
23367                self.skip(); // consume TABLE
23368                if self.check(TokenType::Lt) {
23369                    // BigQuery: RETURNS TABLE <col1 TYPE, col2 TYPE>
23370                    self.skip(); // consume <
23371                    let mut cols = Vec::new();
23372                    loop {
23373                        let col_name = self.expect_identifier()?;
23374                        let col_type = self.parse_data_type()?;
23375                        cols.push(format!(
23376                            "{} {}",
23377                            col_name,
23378                            self.data_type_to_string(&col_type)
23379                        ));
23380                        if !self.match_token(TokenType::Comma) {
23381                            break;
23382                        }
23383                    }
23384                    if !self.match_token(TokenType::Gt) {
23385                        return Err(self.parse_error("Expected > after TABLE column definitions"));
23386                    }
23387                    returns_table_body = Some(format!("TABLE <{}>", cols.join(", ")));
23388                } else if self.check(TokenType::LParen) {
23389                    // Snowflake: RETURNS TABLE(col1 TYPE, col2 TYPE)
23390                    self.skip(); // consume (
23391                    let mut cols = Vec::new();
23392                    loop {
23393                        let col_name = self.expect_identifier()?;
23394                        let col_type = self.parse_data_type()?;
23395                        cols.push(format!(
23396                            "{} {}",
23397                            col_name,
23398                            self.data_type_to_string(&col_type)
23399                        ));
23400                        if !self.match_token(TokenType::Comma) {
23401                            break;
23402                        }
23403                    }
23404                    self.expect(TokenType::RParen)?;
23405                    returns_table_body = Some(format!("TABLE ({})", cols.join(", ")));
23406                } else {
23407                    // TSQL: RETURNS TABLE AS RETURN ...
23408                    return_type = Some(DataType::Custom {
23409                        name: "TABLE".to_string(),
23410                    });
23411                }
23412            } else {
23413                // Use parse_function_return_type to preserve original type names like 'integer'
23414                return_type = Some(self.parse_function_return_type()?);
23415            }
23416        }
23417
23418        let mut deterministic = None;
23419        let mut returns_null_on_null_input = None;
23420        let mut strict = false;
23421        let mut security = None;
23422        let mut body = None;
23423        let mut set_options: Vec<FunctionSetOption> = Vec::new();
23424        let mut property_order: Vec<FunctionPropertyKind> = Vec::new();
23425        let mut options: Vec<Expression> = Vec::new();
23426        let mut environment: Vec<Expression> = Vec::new();
23427        let mut handler: Option<String> = None;
23428        let mut parameter_style: Option<String> = None;
23429
23430        // Parse function options
23431        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
23432            if self.check(TokenType::Returns)
23433                && self.current + 1 < self.tokens.len()
23434                && self.tokens[self.current + 1].token_type == TokenType::Null
23435            {
23436                // RETURNS NULL ON NULL INPUT
23437                self.skip(); // consume RETURNS
23438                self.skip(); // consume NULL
23439                self.match_token(TokenType::On);
23440                self.match_token(TokenType::Null);
23441                self.match_token(TokenType::Input);
23442                returns_null_on_null_input = Some(true);
23443                if !property_order.contains(&FunctionPropertyKind::NullInput) {
23444                    property_order.push(FunctionPropertyKind::NullInput);
23445                }
23446            } else if self.match_token(TokenType::Returns) {
23447                // RETURNS can come after LANGUAGE
23448                return_type = Some(self.parse_data_type()?);
23449            } else if self.match_token(TokenType::Language) {
23450                // Language can be SQL, PLPGSQL, PYTHON, etc.
23451                language = Some(self.expect_identifier_or_keyword()?);
23452                if !property_order.contains(&FunctionPropertyKind::Language) {
23453                    property_order.push(FunctionPropertyKind::Language);
23454                }
23455            } else if self.match_token(TokenType::Not) && self.match_identifier("DETERMINISTIC") {
23456                deterministic = Some(false);
23457                if !property_order.contains(&FunctionPropertyKind::Determinism) {
23458                    property_order.push(FunctionPropertyKind::Determinism);
23459                }
23460            } else if self.match_identifier("DETERMINISTIC") {
23461                deterministic = Some(true);
23462                if !property_order.contains(&FunctionPropertyKind::Determinism) {
23463                    property_order.push(FunctionPropertyKind::Determinism);
23464                }
23465            } else if self.match_identifier("IMMUTABLE") {
23466                deterministic = Some(true);
23467                if !property_order.contains(&FunctionPropertyKind::Determinism) {
23468                    property_order.push(FunctionPropertyKind::Determinism);
23469                }
23470            } else if self.match_identifier("STABLE") || self.match_identifier("VOLATILE") {
23471                deterministic = Some(false);
23472                if !property_order.contains(&FunctionPropertyKind::Determinism) {
23473                    property_order.push(FunctionPropertyKind::Determinism);
23474                }
23475            } else if self.match_identifier("STRICT") {
23476                returns_null_on_null_input = Some(true);
23477                strict = true;
23478                if !property_order.contains(&FunctionPropertyKind::NullInput) {
23479                    property_order.push(FunctionPropertyKind::NullInput);
23480                }
23481            } else if self.match_identifier("CALLED") {
23482                self.match_token(TokenType::On);
23483                self.match_token(TokenType::Null);
23484                self.match_token(TokenType::Input);
23485                returns_null_on_null_input = Some(false);
23486                if !property_order.contains(&FunctionPropertyKind::NullInput) {
23487                    property_order.push(FunctionPropertyKind::NullInput);
23488                }
23489            } else if self.match_identifier("SECURITY") {
23490                if self.match_identifier("DEFINER") {
23491                    security = Some(FunctionSecurity::Definer);
23492                } else if self.match_identifier("INVOKER") {
23493                    security = Some(FunctionSecurity::Invoker);
23494                }
23495                if !property_order.contains(&FunctionPropertyKind::Security) {
23496                    property_order.push(FunctionPropertyKind::Security);
23497                }
23498            } else if self.match_identifier("CONTAINS") {
23499                // CONTAINS SQL
23500                self.match_identifier("SQL");
23501                sql_data_access = Some(SqlDataAccess::ContainsSql);
23502                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
23503                    property_order.push(FunctionPropertyKind::SqlDataAccess);
23504                }
23505            } else if self.match_identifier("READS") {
23506                // READS SQL DATA
23507                self.match_identifier("SQL");
23508                self.match_identifier("DATA");
23509                sql_data_access = Some(SqlDataAccess::ReadsSqlData);
23510                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
23511                    property_order.push(FunctionPropertyKind::SqlDataAccess);
23512                }
23513            } else if self.match_identifier("MODIFIES") {
23514                // MODIFIES SQL DATA
23515                self.match_identifier("SQL");
23516                self.match_identifier("DATA");
23517                sql_data_access = Some(SqlDataAccess::ModifiesSqlData);
23518                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
23519                    property_order.push(FunctionPropertyKind::SqlDataAccess);
23520                }
23521            } else if self.match_token(TokenType::No) && self.match_identifier("SQL") {
23522                // NO SQL
23523                sql_data_access = Some(SqlDataAccess::NoSql);
23524                if !property_order.contains(&FunctionPropertyKind::SqlDataAccess) {
23525                    property_order.push(FunctionPropertyKind::SqlDataAccess);
23526                }
23527            } else if self.match_token(TokenType::Set) {
23528                // PostgreSQL: SET key = value / SET key TO value / SET key FROM CURRENT
23529                let opt_name = self.expect_identifier_or_keyword()?;
23530                let value = if self.match_token(TokenType::From) {
23531                    // SET key FROM CURRENT
23532                    if !self.match_token(TokenType::Current) {
23533                        return Err(self.parse_error("Expected CURRENT after FROM in SET option"));
23534                    }
23535                    FunctionSetValue::FromCurrent
23536                } else {
23537                    // SET key = value or SET key TO value
23538                    let use_to = self.match_token(TokenType::To);
23539                    if !use_to && !self.match_token(TokenType::Eq) {
23540                        return Err(self.parse_error("Expected = or TO after SET key"));
23541                    }
23542                    // Value can be a string literal or identifier
23543                    let val = if self.check(TokenType::String) {
23544                        let tok = self.advance();
23545                        format!("'{}'", tok.text)
23546                    } else {
23547                        self.expect_identifier_or_keyword()?
23548                    };
23549                    FunctionSetValue::Value { value: val, use_to }
23550                };
23551                set_options.push(FunctionSetOption {
23552                    name: opt_name,
23553                    value,
23554                });
23555                if !property_order.contains(&FunctionPropertyKind::Set) {
23556                    property_order.push(FunctionPropertyKind::Set);
23557                }
23558            } else if self.match_token(TokenType::As) {
23559                // Parse function body: AS RETURN x, AS $$ ... $$, AS BEGIN ... END, AS 'body'
23560                if !property_order.contains(&FunctionPropertyKind::As) {
23561                    property_order.push(FunctionPropertyKind::As);
23562                }
23563                if self.match_identifier("RETURN") {
23564                    // AS RETURN expression (or SELECT statement for TSQL TVFs)
23565                    let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
23566                        // TSQL: AS RETURN SELECT ... for table-valued functions
23567                        self.parse_statement()?
23568                    } else {
23569                        self.parse_expression()?
23570                    };
23571                    body = Some(FunctionBody::Return(expr));
23572                } else if self.check(TokenType::Select) || self.check(TokenType::With) {
23573                    // TSQL: AS SELECT ... for table-valued functions (without RETURN keyword)
23574                    let stmt = self.parse_statement()?;
23575                    body = Some(FunctionBody::Expression(stmt));
23576                } else if self.check(TokenType::DollarString) {
23577                    let tok = self.advance();
23578                    // Parse the dollar string token to extract tag and content
23579                    let (tag, content) = crate::tokens::parse_dollar_string_token(&tok.text);
23580                    body = Some(FunctionBody::DollarQuoted { content, tag });
23581                } else if self.check(TokenType::String) {
23582                    let tok = self.advance();
23583                    body = Some(FunctionBody::StringLiteral(tok.text.clone()));
23584                } else if self.match_token(TokenType::Begin) {
23585                    // Parse BEGIN...END block
23586                    let mut block_content = String::new();
23587                    let mut depth = 1;
23588                    while depth > 0 && !self.is_at_end() {
23589                        let tok = self.advance();
23590                        if tok.token_type == TokenType::Begin {
23591                            depth += 1;
23592                        } else if tok.token_type == TokenType::End {
23593                            depth -= 1;
23594                            if depth == 0 {
23595                                break;
23596                            }
23597                        }
23598                        block_content.push_str(&tok.text);
23599                        block_content.push(' ');
23600                    }
23601                    body = Some(FunctionBody::Block(block_content.trim().to_string()));
23602                } else if self.check(TokenType::Table) {
23603                    // DuckDB: AS TABLE SELECT ... (table macro)
23604                    self.advance(); // consume TABLE
23605                    if return_type.is_none() {
23606                        return_type = Some(DataType::Custom {
23607                            name: "TABLE".to_string(),
23608                        });
23609                    }
23610                    let stmt = self.parse_statement()?;
23611                    body = Some(FunctionBody::Return(stmt));
23612                } else {
23613                    // Expression-based body
23614                    let expr = self.parse_expression()?;
23615                    body = Some(FunctionBody::Expression(expr));
23616                }
23617            } else if self.match_identifier("RETURN") {
23618                // RETURN expression (or SELECT statement for TSQL TVFs)
23619                let expr = if self.check(TokenType::Select) || self.check(TokenType::With) {
23620                    self.parse_statement()?
23621                } else {
23622                    self.parse_expression()?
23623                };
23624                body = Some(FunctionBody::Return(expr));
23625            } else if self.match_identifier("EXTERNAL") {
23626                self.match_identifier("NAME");
23627                let ext_name = if self.check(TokenType::String) {
23628                    let tok = self.advance();
23629                    tok.text.trim_matches('\'').to_string()
23630                } else {
23631                    self.expect_identifier()?
23632                };
23633                body = Some(FunctionBody::External(ext_name));
23634            } else if self.match_identifier("OPTIONS") {
23635                // BigQuery: OPTIONS (key=value, ...) - track in property_order
23636                let parsed_options = self.parse_options_list()?;
23637                options.extend(parsed_options);
23638                if !property_order.contains(&FunctionPropertyKind::Options) {
23639                    property_order.push(FunctionPropertyKind::Options);
23640                }
23641            } else if self.match_identifier("ENVIRONMENT") {
23642                // Databricks: ENVIRONMENT (dependencies = '...', environment_version = '...')
23643                let parsed_env = self.parse_environment_list()?;
23644                environment.extend(parsed_env);
23645                if !property_order.contains(&FunctionPropertyKind::Environment) {
23646                    property_order.push(FunctionPropertyKind::Environment);
23647                }
23648            } else if self.match_identifier("HANDLER") {
23649                // Databricks: HANDLER 'handler_function'
23650                if self.check(TokenType::String) {
23651                    let tok = self.advance();
23652                    handler = Some(tok.text.clone());
23653                }
23654                if !property_order.contains(&FunctionPropertyKind::Handler) {
23655                    property_order.push(FunctionPropertyKind::Handler);
23656                }
23657            } else if self.match_text_seq(&["PARAMETER", "STYLE"]) {
23658                // Databricks: PARAMETER STYLE PANDAS
23659                let style = self.expect_identifier_or_keyword()?;
23660                parameter_style = Some(style.to_ascii_uppercase());
23661                if !property_order.contains(&FunctionPropertyKind::ParameterStyle) {
23662                    property_order.push(FunctionPropertyKind::ParameterStyle);
23663                }
23664            } else if self.check_identifier("SQL")
23665                && self.current + 1 < self.tokens.len()
23666                && self.tokens[self.current + 1]
23667                    .text
23668                    .eq_ignore_ascii_case("SECURITY")
23669            {
23670                // SQL SECURITY DEFINER/INVOKER
23671                self.skip(); // consume SQL
23672                self.skip(); // consume SECURITY
23673                if self.match_identifier("DEFINER") {
23674                    security = Some(FunctionSecurity::Definer);
23675                } else if self.match_identifier("INVOKER") {
23676                    security = Some(FunctionSecurity::Invoker);
23677                }
23678                if !property_order.contains(&FunctionPropertyKind::Security) {
23679                    property_order.push(FunctionPropertyKind::Security);
23680                }
23681            } else if self.check(TokenType::Select) || self.check(TokenType::With) {
23682                // Bare SELECT/WITH body (without AS keyword) - e.g., MySQL
23683                let stmt = self.parse_statement()?;
23684                body = Some(FunctionBody::Expression(stmt));
23685                if !property_order.contains(&FunctionPropertyKind::As) {
23686                    property_order.push(FunctionPropertyKind::As);
23687                }
23688            } else {
23689                break;
23690            }
23691        }
23692
23693        // BigQuery: OPTIONS (key=value, ...) can also appear after AS body (legacy position)
23694        if options.is_empty() && self.match_identifier("OPTIONS") {
23695            let parsed_options = self.parse_options_list()?;
23696            options.extend(parsed_options);
23697            if !property_order.contains(&FunctionPropertyKind::Options) {
23698                property_order.push(FunctionPropertyKind::Options);
23699            }
23700        }
23701
23702        Ok(Expression::CreateFunction(Box::new(CreateFunction {
23703            name,
23704            parameters,
23705            return_type,
23706            body,
23707            or_replace,
23708            or_alter,
23709            if_not_exists,
23710            temporary,
23711            language,
23712            deterministic,
23713            returns_null_on_null_input,
23714            security,
23715            has_parens,
23716            sql_data_access,
23717            returns_table_body,
23718            language_first,
23719            set_options,
23720            strict,
23721            options,
23722            is_table_function,
23723            property_order,
23724            environment,
23725            handler,
23726            parameter_style,
23727        })))
23728    }
23729
23730    /// Parse function parameters
23731    fn parse_function_parameters(&mut self) -> Result<Vec<FunctionParameter>> {
23732        let mut params = Vec::new();
23733
23734        if self.check(TokenType::RParen) {
23735            return Ok(params);
23736        }
23737
23738        loop {
23739            let mut mode = None;
23740            let mut mode_text: Option<String> = None;
23741
23742            // Check for parameter mode (IN, OUT, INOUT, VARIADIC)
23743            // Note: OUT, INOUT, VARIADIC are tokenized as Var, not as dedicated keywords
23744            if self.match_token(TokenType::In) {
23745                // IN or IN OUT
23746                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OUT") {
23747                    let out_text = self.advance().text.clone(); // consume OUT
23748                    mode_text = Some(format!("IN {}", out_text));
23749                    mode = Some(ParameterMode::InOut);
23750                } else {
23751                    mode_text = Some("IN".to_string());
23752                    mode = Some(ParameterMode::In);
23753                }
23754            } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("OUT") {
23755                let text = self.advance().text.clone();
23756                mode_text = Some(text);
23757                mode = Some(ParameterMode::Out);
23758            } else if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("INOUT") {
23759                let text = self.advance().text.clone();
23760                mode_text = Some(text);
23761                mode = Some(ParameterMode::InOut);
23762            } else if self.check(TokenType::Var)
23763                && self.peek().text.eq_ignore_ascii_case("VARIADIC")
23764            {
23765                let text = self.advance().text.clone();
23766                mode_text = Some(text);
23767                mode = Some(ParameterMode::Variadic);
23768            }
23769
23770            // Try to parse name and type
23771            // After a mode keyword (VARIADIC, OUT, etc.), the next thing could be:
23772            //   - a type directly (e.g., VARIADIC INT[], OUT INT)
23773            //   - a name then a type (e.g., VARIADIC a INT[], OUT result INT)
23774            //
23775            // Strategy: use backtracking. Save position, try parsing as data type.
23776            // If the result is followed by , or ) or DEFAULT, it was a type-only param.
23777            // Otherwise, restore position and parse as name + type.
23778            let (name, data_type) = if mode.is_some() {
23779                let saved = self.current;
23780                // Try parsing as a data type directly
23781                let type_result = self.parse_data_type();
23782                if let Ok(dt) = type_result {
23783                    if self.check(TokenType::Comma)
23784                        || self.check(TokenType::RParen)
23785                        || self.check(TokenType::Default)
23786                        || self.check(TokenType::Eq)
23787                    {
23788                        // Successfully parsed as a type-only parameter
23789                        (None, dt)
23790                    } else {
23791                        // Not followed by comma/rparen — restore and parse as name + type
23792                        self.current = saved;
23793                        let first_ident =
23794                            if self.check(TokenType::Input) || self.check(TokenType::Output) {
23795                                let token = self.advance();
23796                                Identifier {
23797                                    name: token.text,
23798                                    quoted: false,
23799                                    trailing_comments: Vec::new(),
23800                                    span: None,
23801                                }
23802                            } else {
23803                                self.expect_identifier_with_quoted()?
23804                            };
23805                        self.match_token(TokenType::As);
23806                        let dt = self.parse_data_type()?;
23807                        (Some(first_ident), dt)
23808                    }
23809                } else {
23810                    // Type parse failed — restore and try as name + type
23811                    self.current = saved;
23812                    let first_ident =
23813                        if self.check(TokenType::Input) || self.check(TokenType::Output) {
23814                            let token = self.advance();
23815                            Identifier {
23816                                name: token.text,
23817                                quoted: false,
23818                                trailing_comments: Vec::new(),
23819                                span: None,
23820                            }
23821                        } else {
23822                            self.expect_identifier_with_quoted()?
23823                        };
23824                    if self.check(TokenType::Comma)
23825                        || self.check(TokenType::RParen)
23826                        || self.check(TokenType::Default)
23827                    {
23828                        (None, self.identifier_to_datatype(&first_ident.name)?)
23829                    } else {
23830                        self.match_token(TokenType::As);
23831                        let dt = self.parse_data_type()?;
23832                        (Some(first_ident), dt)
23833                    }
23834                }
23835            } else {
23836                // No mode keyword — original logic
23837                // Handle keywords like INPUT that may be used as parameter names
23838                let first_ident = if self.check(TokenType::Input) || self.check(TokenType::Output) {
23839                    let token = self.advance();
23840                    Identifier {
23841                        name: token.text,
23842                        quoted: false,
23843                        trailing_comments: Vec::new(),
23844                        span: None,
23845                    }
23846                } else {
23847                    self.expect_identifier_with_quoted()?
23848                };
23849
23850                // Check if next token is a type or if this was the type
23851                if self.check(TokenType::Comma)
23852                    || self.check(TokenType::RParen)
23853                    || self.check(TokenType::Default)
23854                {
23855                    // This was the type, no name
23856                    (None, self.identifier_to_datatype(&first_ident.name)?)
23857                } else {
23858                    // This was the name, next is type
23859                    // TSQL allows: @param AS type (optional AS keyword)
23860                    self.match_token(TokenType::As);
23861                    let dt = self.parse_data_type()?;
23862                    (Some(first_ident), dt)
23863                }
23864            };
23865
23866            let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq)
23867            {
23868                Some(self.parse_expression()?)
23869            } else {
23870                None
23871            };
23872
23873            params.push(FunctionParameter {
23874                name,
23875                data_type,
23876                mode,
23877                default,
23878                mode_text: mode_text.clone(),
23879            });
23880
23881            if !self.match_token(TokenType::Comma) {
23882                break;
23883            }
23884        }
23885
23886        Ok(params)
23887    }
23888
23889    /// Parse TSQL-style unparenthesized procedure parameters
23890    /// Format: @param1 TYPE, @param2 TYPE, ... AS
23891    fn parse_tsql_procedure_params(&mut self) -> Result<Vec<FunctionParameter>> {
23892        let mut params = Vec::new();
23893        loop {
23894            if !self.check(TokenType::Var) {
23895                break;
23896            }
23897            let name = self.advance().text.clone();
23898            // Skip optional AS keyword between name and type
23899            self.match_token(TokenType::As);
23900            let data_type = self.parse_data_type()?;
23901            let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq)
23902            {
23903                Some(self.parse_expression()?)
23904            } else {
23905                None
23906            };
23907            params.push(FunctionParameter {
23908                name: Some(Identifier::new(name)),
23909                data_type,
23910                mode: None,
23911                default,
23912                mode_text: None,
23913            });
23914            if !self.match_token(TokenType::Comma) {
23915                break;
23916            }
23917        }
23918        Ok(params)
23919    }
23920
23921    /// Convert identifier to DataType for function parameters.
23922    /// Preserves the original identifier name to maintain exact type name as written.
23923    /// This matches Python sqlglot's behavior where function parameter types like 'integer'
23924    /// are stored as Identifiers rather than normalized DataTypes.
23925    fn identifier_to_datatype(&self, ident: &str) -> Result<DataType> {
23926        // Always use DataType::Custom to preserve the exact type name as written.
23927        // This is important for identity tests where e.g. 'integer' should not be normalized to 'INT'.
23928        Ok(DataType::Custom {
23929            name: ident.to_string(),
23930        })
23931    }
23932
23933    /// Parse a data type for function RETURNS clause, preserving original type names.
23934    /// For simple type names like 'integer', preserves the original name rather than
23935    /// normalizing to INT. This matches Python sqlglot's behavior.
23936    /// For MySQL, uses standard parse_data_type() to ensure proper type mapping (e.g., VARCHAR -> TEXT).
23937    fn parse_function_return_type(&mut self) -> Result<DataType> {
23938        // MySQL needs standard data type parsing for proper type mapping
23939        if matches!(
23940            self.config.dialect,
23941            Some(crate::dialects::DialectType::MySQL)
23942        ) {
23943            return self.parse_data_type();
23944        }
23945
23946        // Check if it's a simple identifier that could be a type name
23947        if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
23948            && !self.check_next(TokenType::LParen)  // Not a parameterized type like VARCHAR(10)
23949            && !self.check_next(TokenType::LBracket)
23950        // Not an array type
23951        {
23952            let type_name = self.advance().text.clone();
23953            // Check if the next token indicates we should use parse_data_type instead
23954            // For complex types, fall through to parse_data_type
23955            return Ok(DataType::Custom { name: type_name });
23956        }
23957
23958        // For complex types, use standard parsing
23959        self.parse_data_type()
23960    }
23961
23962    /// Parse DROP FUNCTION statement
23963    fn parse_drop_function(&mut self) -> Result<Expression> {
23964        self.expect(TokenType::Function)?;
23965
23966        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
23967        let name = self.parse_table_ref()?;
23968
23969        // Optional parameter types for overloaded functions
23970        let parameters = if self.match_token(TokenType::LParen) {
23971            let mut types = Vec::new();
23972            if !self.check(TokenType::RParen) {
23973                loop {
23974                    types.push(self.parse_data_type()?);
23975                    if !self.match_token(TokenType::Comma) {
23976                        break;
23977                    }
23978                }
23979            }
23980            self.expect(TokenType::RParen)?;
23981            Some(types)
23982        } else {
23983            None
23984        };
23985
23986        let cascade = self.match_token(TokenType::Cascade);
23987        if !cascade {
23988            self.match_token(TokenType::Restrict);
23989        }
23990
23991        Ok(Expression::DropFunction(Box::new(DropFunction {
23992            name,
23993            parameters,
23994            if_exists,
23995            cascade,
23996        })))
23997    }
23998
23999    /// Parse CREATE PROCEDURE statement
24000    fn parse_create_procedure(&mut self, or_replace: bool, or_alter: bool) -> Result<Expression> {
24001        // Check if PROC shorthand was used before consuming the token
24002        let use_proc_keyword = self.peek().text.eq_ignore_ascii_case("PROC");
24003        self.expect(TokenType::Procedure)?;
24004
24005        let if_not_exists =
24006            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24007        let name = self.parse_table_ref()?;
24008
24009        // Parse parameters (optional parentheses for TSQL)
24010        let (parameters, has_parens) = if self.match_token(TokenType::LParen) {
24011            let params = self.parse_function_parameters()?;
24012            self.expect(TokenType::RParen)?;
24013            (params, true)
24014        } else if self.check(TokenType::Var) && !self.check(TokenType::As) {
24015            // TSQL: CREATE PROCEDURE foo @a INTEGER, @b INTEGER AS ...
24016            // Parameters without parentheses
24017            let params = self.parse_tsql_procedure_params()?;
24018            (params, false)
24019        } else {
24020            (Vec::new(), false)
24021        };
24022
24023        let mut language = None;
24024        let mut security = None;
24025        let mut body = None;
24026        let mut return_type = None;
24027        let mut execute_as = None;
24028        let mut with_options: Vec<String> = Vec::new();
24029
24030        // Parse procedure options
24031        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24032            if self.match_token(TokenType::Returns) {
24033                // RETURNS type (Snowflake)
24034                return_type = Some(self.parse_data_type()?);
24035            } else if self.match_identifier("EXECUTE") || self.match_token(TokenType::Execute) {
24036                // EXECUTE AS CALLER/OWNER (Snowflake)
24037                if self.match_token(TokenType::As) {
24038                    if self.match_identifier("CALLER") {
24039                        execute_as = Some("CALLER".to_string());
24040                    } else if self.match_identifier("OWNER") {
24041                        execute_as = Some("OWNER".to_string());
24042                    } else if self.match_identifier("SELF") {
24043                        execute_as = Some("SELF".to_string());
24044                    }
24045                }
24046            } else if self.match_token(TokenType::Language) {
24047                // Language can be SQL, PLPGSQL, PYTHON, etc.
24048                language = Some(self.expect_identifier_or_keyword()?);
24049            } else if self.match_identifier("SECURITY") {
24050                if self.match_identifier("DEFINER") {
24051                    security = Some(FunctionSecurity::Definer);
24052                } else if self.match_identifier("INVOKER") {
24053                    security = Some(FunctionSecurity::Invoker);
24054                }
24055            } else if self.match_token(TokenType::With) {
24056                // TSQL: WITH option1, option2, ... AS body
24057                // Options: ENCRYPTION, RECOMPILE, SCHEMABINDING, NATIVE_COMPILATION,
24058                //          EXECUTE AS {OWNER|SELF|CALLER|'username'}
24059                loop {
24060                    if self.match_identifier("EXECUTE") || self.match_token(TokenType::Execute) {
24061                        // EXECUTE AS {OWNER|SELF|CALLER|'username'}
24062                        self.expect(TokenType::As)?;
24063                        if self.check(TokenType::String) {
24064                            let tok = self.advance();
24065                            with_options.push(format!("EXECUTE AS '{}'", tok.text));
24066                        } else {
24067                            let ident = self.expect_identifier_or_keyword()?;
24068                            with_options.push(format!("EXECUTE AS {}", ident.to_ascii_uppercase()));
24069                        }
24070                    } else {
24071                        let opt = self.expect_identifier_or_keyword()?;
24072                        with_options.push(opt.to_ascii_uppercase());
24073                    }
24074                    if !self.match_token(TokenType::Comma) {
24075                        break;
24076                    }
24077                }
24078            } else if self.match_token(TokenType::As) {
24079                // Parse procedure body
24080                if self.check(TokenType::String) {
24081                    // TokenType::String means single-quoted - tokenizer strips quotes
24082                    let tok = self.advance();
24083                    body = Some(FunctionBody::StringLiteral(tok.text.clone()));
24084                } else if self.match_token(TokenType::Begin) {
24085                    // Parse BEGIN ... END block as a list of statements
24086                    let mut statements = Vec::new();
24087                    while !self.check(TokenType::End) && !self.is_at_end() {
24088                        // Skip optional semicolons between statements
24089                        while self.match_token(TokenType::Semicolon) {}
24090                        if self.check(TokenType::End) {
24091                            break;
24092                        }
24093                        statements.push(self.parse_statement()?);
24094                        // Skip optional semicolon after statement
24095                        self.match_token(TokenType::Semicolon);
24096                    }
24097                    self.expect(TokenType::End)?;
24098                    body = Some(FunctionBody::Statements(statements));
24099                } else {
24100                    // TSQL: AS <statement> (e.g., AS SELECT 1)
24101                    let stmt = self.parse_statement()?;
24102                    body = Some(FunctionBody::Expression(stmt));
24103                }
24104            } else {
24105                break;
24106            }
24107        }
24108
24109        Ok(Expression::CreateProcedure(Box::new(CreateProcedure {
24110            name,
24111            parameters,
24112            body,
24113            or_replace,
24114            or_alter,
24115            if_not_exists,
24116            language,
24117            security,
24118            return_type,
24119            execute_as,
24120            with_options,
24121            has_parens,
24122            use_proc_keyword,
24123        })))
24124    }
24125
24126    /// Parse DROP PROCEDURE statement
24127    fn parse_drop_procedure(&mut self) -> Result<Expression> {
24128        self.expect(TokenType::Procedure)?;
24129
24130        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24131        let name = self.parse_table_ref()?;
24132
24133        let parameters = if self.match_token(TokenType::LParen) {
24134            let mut types = Vec::new();
24135            if !self.check(TokenType::RParen) {
24136                loop {
24137                    types.push(self.parse_data_type()?);
24138                    if !self.match_token(TokenType::Comma) {
24139                        break;
24140                    }
24141                }
24142            }
24143            self.expect(TokenType::RParen)?;
24144            Some(types)
24145        } else {
24146            None
24147        };
24148
24149        let cascade = self.match_token(TokenType::Cascade);
24150        if !cascade {
24151            self.match_token(TokenType::Restrict);
24152        }
24153
24154        Ok(Expression::DropProcedure(Box::new(DropProcedure {
24155            name,
24156            parameters,
24157            if_exists,
24158            cascade,
24159        })))
24160    }
24161
24162    /// Parse CREATE SEQUENCE statement
24163    fn parse_create_sequence(&mut self, temporary: bool, or_replace: bool) -> Result<Expression> {
24164        self.expect(TokenType::Sequence)?;
24165
24166        let if_not_exists =
24167            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24168        let name = self.parse_table_ref()?;
24169
24170        let mut seq = CreateSequence {
24171            name,
24172            if_not_exists,
24173            temporary,
24174            or_replace,
24175            as_type: None,
24176            increment: None,
24177            minvalue: None,
24178            maxvalue: None,
24179            start: None,
24180            cache: None,
24181            cycle: false,
24182            owned_by: None,
24183            owned_by_none: false,
24184            order: None,
24185            comment: None,
24186            sharing: None,
24187            scale_modifier: None,
24188            shard_modifier: None,
24189            property_order: Vec::new(),
24190        };
24191
24192        // Parse optional AS <type> clause (e.g., AS SMALLINT, AS BIGINT)
24193        if self.match_token(TokenType::As) {
24194            seq.as_type = Some(self.parse_data_type()?);
24195        }
24196
24197        // Parse sequence options
24198        // Handle optional WITH keyword before options (Snowflake: WITH START = n INCREMENT = n)
24199        self.match_token(TokenType::With);
24200
24201        loop {
24202            // Skip optional commas between options (Snowflake uses comma-separated options)
24203            self.match_token(TokenType::Comma);
24204
24205            if self.is_at_end() || self.check(TokenType::Semicolon) {
24206                break;
24207            }
24208
24209            if self.match_token(TokenType::Increment) || self.match_identifier("INCREMENT") {
24210                self.match_token(TokenType::By);
24211                self.match_token(TokenType::Eq); // Snowflake uses = instead of BY
24212                seq.increment = Some(self.parse_signed_integer()?);
24213                seq.property_order.push(SeqPropKind::Increment);
24214            } else if self.match_token(TokenType::Minvalue) {
24215                seq.minvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
24216                seq.property_order.push(SeqPropKind::Minvalue);
24217            } else if self.match_keywords(&[TokenType::No, TokenType::Minvalue]) {
24218                seq.minvalue = Some(SequenceBound::None);
24219                seq.property_order.push(SeqPropKind::Minvalue);
24220            } else if self.match_identifier("NOMINVALUE") {
24221                seq.minvalue = Some(SequenceBound::None);
24222                seq.property_order.push(SeqPropKind::NoMinvalueWord);
24223            } else if self.match_token(TokenType::Maxvalue) {
24224                seq.maxvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
24225                seq.property_order.push(SeqPropKind::Maxvalue);
24226            } else if self.match_keywords(&[TokenType::No, TokenType::Maxvalue]) {
24227                seq.maxvalue = Some(SequenceBound::None);
24228                seq.property_order.push(SeqPropKind::Maxvalue);
24229            } else if self.match_identifier("NOMAXVALUE") {
24230                seq.maxvalue = Some(SequenceBound::None);
24231                seq.property_order.push(SeqPropKind::NoMaxvalueWord);
24232            } else if self.match_token(TokenType::Start) {
24233                self.match_token(TokenType::With);
24234                self.match_token(TokenType::Eq); // Snowflake uses = instead of WITH
24235                seq.start = Some(self.parse_signed_integer()?);
24236                seq.property_order.push(SeqPropKind::Start);
24237            } else if self.match_token(TokenType::Cache) {
24238                seq.cache = Some(self.parse_signed_integer()?);
24239                seq.property_order.push(SeqPropKind::Cache);
24240            } else if self.match_identifier("NOCACHE") {
24241                // Oracle: NOCACHE (single word)
24242                seq.property_order.push(SeqPropKind::NoCacheWord);
24243            } else if self.match_token(TokenType::Cycle) {
24244                seq.cycle = true;
24245                seq.property_order.push(SeqPropKind::Cycle);
24246            } else if self.match_token(TokenType::NoCycle) {
24247                // NOCYCLE keyword token - preserve as single word
24248                seq.cycle = false;
24249                seq.property_order.push(SeqPropKind::NoCycleWord);
24250            } else if self.match_token(TokenType::No) {
24251                // Two-word NO forms
24252                if self.match_token(TokenType::Cycle) {
24253                    seq.cycle = false;
24254                    seq.property_order.push(SeqPropKind::NoCycle);
24255                } else if self.match_token(TokenType::Cache) || self.match_identifier("CACHE") {
24256                    seq.property_order.push(SeqPropKind::NoCache);
24257                } else if self.match_token(TokenType::Minvalue) {
24258                    seq.minvalue = Some(SequenceBound::None);
24259                    seq.property_order.push(SeqPropKind::Minvalue);
24260                } else if self.match_token(TokenType::Maxvalue) {
24261                    seq.maxvalue = Some(SequenceBound::None);
24262                    seq.property_order.push(SeqPropKind::Maxvalue);
24263                } else {
24264                    // Unexpected token after NO
24265                    break;
24266                }
24267            } else if self.match_token(TokenType::Owned) {
24268                self.expect(TokenType::By)?;
24269                if self.match_identifier("NONE") {
24270                    seq.owned_by = None;
24271                    seq.owned_by_none = true;
24272                } else {
24273                    seq.owned_by = Some(self.parse_table_ref()?);
24274                }
24275                seq.property_order.push(SeqPropKind::OwnedBy);
24276            } else if self.match_token(TokenType::Order) {
24277                // Snowflake/Oracle: ORDER option
24278                seq.order = Some(true);
24279                seq.property_order.push(SeqPropKind::Order);
24280            } else if self.match_identifier("NOORDER") {
24281                // Snowflake/Oracle: NOORDER option
24282                seq.order = Some(false);
24283                seq.property_order.push(SeqPropKind::NoOrder);
24284            } else if self.match_token(TokenType::Comment) || self.match_identifier("COMMENT") {
24285                // Snowflake: COMMENT = 'value'
24286                self.expect(TokenType::Eq)?;
24287                let comment_val = self.expect(TokenType::String)?;
24288                seq.comment = Some(comment_val.text.clone());
24289                seq.property_order.push(SeqPropKind::Comment);
24290            } else if self.match_identifier("SHARING") {
24291                // Oracle: SHARING=value
24292                self.expect(TokenType::Eq)?;
24293                let val = self.expect_identifier_or_keyword()?;
24294                seq.sharing = Some(val);
24295                seq.property_order.push(SeqPropKind::Sharing);
24296            } else if self.match_identifier("NOKEEP") {
24297                seq.property_order.push(SeqPropKind::NoKeep);
24298            } else if self.match_token(TokenType::Keep) || self.match_identifier("KEEP") {
24299                seq.property_order.push(SeqPropKind::Keep);
24300            } else if self.match_identifier("SCALE") {
24301                let modifier = if self.match_identifier("EXTEND") {
24302                    "EXTEND".to_string()
24303                } else if self.match_identifier("NOEXTEND") {
24304                    "NOEXTEND".to_string()
24305                } else {
24306                    String::new()
24307                };
24308                seq.scale_modifier = Some(modifier);
24309                seq.property_order.push(SeqPropKind::Scale);
24310            } else if self.match_identifier("NOSCALE") {
24311                seq.property_order.push(SeqPropKind::NoScale);
24312            } else if self.match_identifier("SHARD") {
24313                let modifier = if self.match_identifier("EXTEND") {
24314                    "EXTEND".to_string()
24315                } else if self.match_identifier("NOEXTEND") {
24316                    "NOEXTEND".to_string()
24317                } else {
24318                    String::new()
24319                };
24320                seq.shard_modifier = Some(modifier);
24321                seq.property_order.push(SeqPropKind::Shard);
24322            } else if self.match_identifier("NOSHARD") {
24323                seq.property_order.push(SeqPropKind::NoShard);
24324            } else if self.match_identifier("SESSION") {
24325                seq.property_order.push(SeqPropKind::Session);
24326            } else if self.match_identifier("GLOBAL") {
24327                seq.property_order.push(SeqPropKind::Global);
24328            } else {
24329                break;
24330            }
24331        }
24332
24333        Ok(Expression::CreateSequence(Box::new(seq)))
24334    }
24335
24336    /// Parse a signed integer (positive or negative)
24337    fn parse_signed_integer(&mut self) -> Result<i64> {
24338        let negative = self.match_token(TokenType::Dash);
24339        let tok = self.expect(TokenType::Number)?;
24340        let value: i64 = tok
24341            .text
24342            .parse()
24343            .map_err(|_| self.parse_error(format!("Invalid integer: {}", tok.text)))?;
24344        Ok(if negative { -value } else { value })
24345    }
24346
24347    /// Parse DROP SEQUENCE statement
24348    fn parse_drop_sequence(&mut self) -> Result<Expression> {
24349        self.expect(TokenType::Sequence)?;
24350
24351        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24352        let name = self.parse_table_ref()?;
24353
24354        let cascade = self.match_token(TokenType::Cascade);
24355        if !cascade {
24356            self.match_token(TokenType::Restrict);
24357        }
24358
24359        Ok(Expression::DropSequence(Box::new(DropSequence {
24360            name,
24361            if_exists,
24362            cascade,
24363        })))
24364    }
24365
24366    /// Parse ALTER SEQUENCE statement
24367    fn parse_alter_sequence(&mut self) -> Result<Expression> {
24368        self.expect(TokenType::Sequence)?;
24369
24370        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24371        let name = self.parse_table_ref()?;
24372
24373        let mut seq = AlterSequence {
24374            name,
24375            if_exists,
24376            increment: None,
24377            minvalue: None,
24378            maxvalue: None,
24379            start: None,
24380            restart: None,
24381            cache: None,
24382            cycle: None,
24383            owned_by: None,
24384        };
24385
24386        // Parse sequence options
24387        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24388            if self.match_token(TokenType::Increment) || self.match_identifier("INCREMENT") {
24389                self.match_token(TokenType::By);
24390                seq.increment = Some(self.parse_signed_integer()?);
24391            } else if self.match_token(TokenType::Minvalue) {
24392                seq.minvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
24393            } else if self.match_keywords(&[TokenType::No, TokenType::Minvalue]) {
24394                seq.minvalue = Some(SequenceBound::None);
24395            } else if self.match_token(TokenType::Maxvalue) {
24396                seq.maxvalue = Some(SequenceBound::Value(self.parse_signed_integer()?));
24397            } else if self.match_keywords(&[TokenType::No, TokenType::Maxvalue]) {
24398                seq.maxvalue = Some(SequenceBound::None);
24399            } else if self.match_token(TokenType::Start) {
24400                self.match_token(TokenType::With);
24401                seq.start = Some(self.parse_signed_integer()?);
24402            } else if self.match_token(TokenType::Restart) {
24403                if self.match_token(TokenType::With)
24404                    || self.check(TokenType::Number)
24405                    || self.check(TokenType::Dash)
24406                {
24407                    seq.restart = Some(Some(self.parse_signed_integer()?));
24408                } else {
24409                    seq.restart = Some(None);
24410                }
24411            } else if self.match_token(TokenType::Cache) {
24412                seq.cache = Some(self.parse_signed_integer()?);
24413            } else if self.match_token(TokenType::Cycle) {
24414                seq.cycle = Some(true);
24415            } else if self.match_token(TokenType::NoCycle) {
24416                seq.cycle = Some(false);
24417            } else if self.match_token(TokenType::Owned) {
24418                self.expect(TokenType::By)?;
24419                if self.match_identifier("NONE") {
24420                    seq.owned_by = Some(None);
24421                } else {
24422                    seq.owned_by = Some(Some(self.parse_table_ref()?));
24423                }
24424            } else {
24425                break;
24426            }
24427        }
24428
24429        Ok(Expression::AlterSequence(Box::new(seq)))
24430    }
24431
24432    /// Parse CREATE TRIGGER statement
24433    fn parse_create_trigger(
24434        &mut self,
24435        or_replace: bool,
24436        or_alter: bool,
24437        constraint: bool,
24438        create_pos: usize,
24439    ) -> Result<Expression> {
24440        self.expect(TokenType::Trigger)?;
24441
24442        let name = self.expect_identifier_with_quoted()?;
24443
24444        // TSQL triggers: CREATE TRIGGER name ON table AFTER INSERT AS BEGIN...END
24445        // These have ON before timing, unlike standard triggers.
24446        // Fall back to Command for these (matches Python sqlglot behavior).
24447        if self.check(TokenType::On) && !constraint {
24448            self.current = create_pos;
24449            return self.fallback_to_command(create_pos);
24450        }
24451
24452        // Parse timing (BEFORE, AFTER, INSTEAD OF)
24453        let timing = if self.match_token(TokenType::Before) {
24454            TriggerTiming::Before
24455        } else if self.match_token(TokenType::After) {
24456            TriggerTiming::After
24457        } else if self.match_token(TokenType::Instead) {
24458            self.expect(TokenType::Of)?;
24459            TriggerTiming::InsteadOf
24460        } else {
24461            // Fall back to Command for unknown trigger syntax
24462            self.current = create_pos;
24463            return self.fallback_to_command(create_pos);
24464        };
24465
24466        // Parse events
24467        let mut events = Vec::new();
24468        loop {
24469            if self.match_token(TokenType::Insert) {
24470                events.push(TriggerEvent::Insert);
24471            } else if self.match_token(TokenType::Update) {
24472                if self.match_token(TokenType::Of) {
24473                    let mut cols = Vec::new();
24474                    loop {
24475                        cols.push(Identifier::new(self.expect_identifier()?));
24476                        if !self.match_token(TokenType::Comma) {
24477                            break;
24478                        }
24479                    }
24480                    events.push(TriggerEvent::Update(Some(cols)));
24481                } else {
24482                    events.push(TriggerEvent::Update(None));
24483                }
24484            } else if self.match_token(TokenType::Delete) {
24485                events.push(TriggerEvent::Delete);
24486            } else if self.match_token(TokenType::Truncate) {
24487                events.push(TriggerEvent::Truncate);
24488            } else {
24489                break;
24490            }
24491
24492            if !self.match_token(TokenType::Or) {
24493                break;
24494            }
24495        }
24496
24497        self.expect(TokenType::On)?;
24498        let table = self.parse_table_ref()?;
24499
24500        // Parse optional REFERENCING clause (for non-constraint triggers)
24501        let referencing = if !constraint && self.match_token(TokenType::Referencing) {
24502            let mut ref_clause = TriggerReferencing {
24503                old_table: None,
24504                new_table: None,
24505                old_row: None,
24506                new_row: None,
24507            };
24508            while self.match_token(TokenType::Old) || self.match_token(TokenType::New) {
24509                let is_old = self.previous().token_type == TokenType::Old;
24510                let is_table = self.match_token(TokenType::Table);
24511                let _is_row = !is_table && self.match_token(TokenType::Row);
24512                self.match_token(TokenType::As);
24513                let alias = Identifier::new(self.expect_identifier()?);
24514
24515                if is_old {
24516                    if is_table {
24517                        ref_clause.old_table = Some(alias);
24518                    } else {
24519                        ref_clause.old_row = Some(alias);
24520                    }
24521                } else {
24522                    if is_table {
24523                        ref_clause.new_table = Some(alias);
24524                    } else {
24525                        ref_clause.new_row = Some(alias);
24526                    }
24527                }
24528            }
24529            Some(ref_clause)
24530        } else {
24531            None
24532        };
24533
24534        // Parse deferrable options for constraint triggers (comes before FOR EACH ROW in PostgreSQL)
24535        let mut deferrable = None;
24536        let mut initially_deferred = None;
24537        if constraint {
24538            if self.match_identifier("DEFERRABLE") {
24539                deferrable = Some(true);
24540            } else if self.match_keywords(&[TokenType::Not, TokenType::Identifier]) {
24541                // NOT DEFERRABLE
24542                deferrable = Some(false);
24543            }
24544            if self.match_identifier("INITIALLY") {
24545                if self.match_identifier("DEFERRED") {
24546                    initially_deferred = Some(true);
24547                } else if self.match_identifier("IMMEDIATE") {
24548                    initially_deferred = Some(false);
24549                }
24550            }
24551        }
24552
24553        // Parse FOR EACH ROW/STATEMENT (optional)
24554        let for_each = if self.match_token(TokenType::For) {
24555            self.match_token(TokenType::Each);
24556            if self.match_token(TokenType::Row) {
24557                Some(TriggerForEach::Row)
24558            } else if self.match_token(TokenType::Statement) {
24559                Some(TriggerForEach::Statement)
24560            } else {
24561                Some(TriggerForEach::Row)
24562            }
24563        } else {
24564            None
24565        };
24566
24567        // Parse optional WHEN clause (parentheses are optional, e.g. SQLite)
24568        let (when, when_paren) = if self.match_token(TokenType::When) {
24569            let has_paren = self.match_token(TokenType::LParen);
24570            let expr = self.parse_expression()?;
24571            if has_paren {
24572                self.expect(TokenType::RParen)?;
24573            }
24574            (Some(expr), has_paren)
24575        } else {
24576            (None, false)
24577        };
24578
24579        // Parse trigger body
24580        let body = if self.match_token(TokenType::Execute) {
24581            self.match_token(TokenType::Function);
24582            self.match_token(TokenType::Procedure);
24583            let func_name = self.parse_table_ref()?;
24584            self.expect(TokenType::LParen)?;
24585            let mut args = Vec::new();
24586            if !self.check(TokenType::RParen) {
24587                loop {
24588                    args.push(self.parse_expression()?);
24589                    if !self.match_token(TokenType::Comma) {
24590                        break;
24591                    }
24592                }
24593            }
24594            self.expect(TokenType::RParen)?;
24595            TriggerBody::Execute {
24596                function: func_name,
24597                args,
24598            }
24599        } else if self.match_token(TokenType::Begin) {
24600            // Record start position (first token after BEGIN)
24601            let body_start = if !self.is_at_end() {
24602                self.tokens[self.current].span.start
24603            } else {
24604                0
24605            };
24606            let mut depth = 1;
24607            while depth > 0 && !self.is_at_end() {
24608                let tok = self.advance();
24609                if tok.token_type == TokenType::Begin {
24610                    depth += 1;
24611                } else if tok.token_type == TokenType::End {
24612                    depth -= 1;
24613                    if depth == 0 {
24614                        break;
24615                    }
24616                }
24617            }
24618            // Extract verbatim text from source if available
24619            let block_content = if let Some(ref source) = self.source {
24620                // End position is the start of the END token
24621                let body_end = if self.current > 0 {
24622                    self.tokens[self.current - 1].span.start
24623                } else {
24624                    body_start
24625                };
24626                source[body_start..body_end].trim().to_string()
24627            } else {
24628                // Fallback: no source available
24629                String::new()
24630            };
24631            TriggerBody::Block(block_content)
24632        } else {
24633            return Err(self.parse_error("Expected EXECUTE or BEGIN in trigger body"));
24634        };
24635
24636        Ok(Expression::CreateTrigger(Box::new(CreateTrigger {
24637            name,
24638            table,
24639            timing,
24640            events,
24641            for_each,
24642            when,
24643            when_paren,
24644            body,
24645            or_replace,
24646            or_alter,
24647            constraint,
24648            deferrable,
24649            initially_deferred,
24650            referencing,
24651        })))
24652    }
24653
24654    /// Parse DROP TRIGGER statement
24655    fn parse_drop_trigger(&mut self) -> Result<Expression> {
24656        self.expect(TokenType::Trigger)?;
24657
24658        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24659        let name = Identifier::new(self.expect_identifier()?);
24660
24661        let table = if self.match_token(TokenType::On) {
24662            Some(self.parse_table_ref()?)
24663        } else {
24664            None
24665        };
24666
24667        let cascade = self.match_token(TokenType::Cascade);
24668        if !cascade {
24669            self.match_token(TokenType::Restrict);
24670        }
24671
24672        Ok(Expression::DropTrigger(Box::new(DropTrigger {
24673            name,
24674            table,
24675            if_exists,
24676            cascade,
24677        })))
24678    }
24679
24680    /// Parse CREATE TYPE statement
24681    fn parse_create_type(&mut self) -> Result<Expression> {
24682        self.expect(TokenType::Type)?;
24683
24684        let if_not_exists =
24685            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24686        let name = self.parse_table_ref()?;
24687
24688        self.expect(TokenType::As)?;
24689
24690        let definition = if self.match_token(TokenType::Enum) {
24691            // ENUM type
24692            self.expect(TokenType::LParen)?;
24693            let mut values = Vec::new();
24694            loop {
24695                let tok = self.expect(TokenType::String)?;
24696                values.push(tok.text.trim_matches('\'').to_string());
24697                if !self.match_token(TokenType::Comma) {
24698                    break;
24699                }
24700            }
24701            self.expect(TokenType::RParen)?;
24702            TypeDefinition::Enum(values)
24703        } else if self.match_token(TokenType::LParen) {
24704            // Composite type
24705            let mut attrs = Vec::new();
24706            loop {
24707                let attr_name = Identifier::new(self.expect_identifier()?);
24708                let data_type = self.parse_data_type()?;
24709                let collate = if self.match_identifier("COLLATE") {
24710                    Some(Identifier::new(self.expect_identifier()?))
24711                } else {
24712                    None
24713                };
24714                attrs.push(TypeAttribute {
24715                    name: attr_name,
24716                    data_type,
24717                    collate,
24718                });
24719                if !self.match_token(TokenType::Comma) {
24720                    break;
24721                }
24722            }
24723            self.expect(TokenType::RParen)?;
24724            TypeDefinition::Composite(attrs)
24725        } else if self.match_token(TokenType::Range) {
24726            // Range type
24727            self.expect(TokenType::LParen)?;
24728            self.match_identifier("SUBTYPE");
24729            self.match_token(TokenType::Eq);
24730            let subtype = self.parse_data_type()?;
24731
24732            let mut subtype_diff = None;
24733            let mut canonical = None;
24734
24735            while self.match_token(TokenType::Comma) {
24736                if self.match_identifier("SUBTYPE_DIFF") {
24737                    self.match_token(TokenType::Eq);
24738                    subtype_diff = Some(self.expect_identifier()?);
24739                } else if self.match_identifier("CANONICAL") {
24740                    self.match_token(TokenType::Eq);
24741                    canonical = Some(self.expect_identifier()?);
24742                }
24743            }
24744            self.expect(TokenType::RParen)?;
24745
24746            TypeDefinition::Range {
24747                subtype,
24748                subtype_diff,
24749                canonical,
24750            }
24751        } else {
24752            return Err(
24753                self.parse_error("Expected ENUM, composite type definition, or RANGE after AS")
24754            );
24755        };
24756
24757        Ok(Expression::CreateType(Box::new(CreateType {
24758            name,
24759            definition,
24760            if_not_exists,
24761        })))
24762    }
24763
24764    /// Parse CREATE DOMAIN statement
24765    fn parse_create_domain(&mut self) -> Result<Expression> {
24766        self.expect(TokenType::Domain)?;
24767
24768        let if_not_exists =
24769            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24770        let name = self.parse_table_ref()?;
24771
24772        self.expect(TokenType::As)?;
24773        let base_type = self.parse_data_type()?;
24774
24775        let mut default = None;
24776        let mut constraints = Vec::new();
24777
24778        // Parse domain options
24779        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24780            if self.match_token(TokenType::Default) {
24781                default = Some(self.parse_expression()?);
24782            } else if self.match_token(TokenType::Constraint) {
24783                let constr_name = Some(Identifier::new(self.expect_identifier()?));
24784                self.expect(TokenType::Check)?;
24785                self.expect(TokenType::LParen)?;
24786                let check_expr = self.parse_expression()?;
24787                self.expect(TokenType::RParen)?;
24788                constraints.push(DomainConstraint {
24789                    name: constr_name,
24790                    check: check_expr,
24791                });
24792            } else if self.match_token(TokenType::Check) {
24793                self.expect(TokenType::LParen)?;
24794                let check_expr = self.parse_expression()?;
24795                self.expect(TokenType::RParen)?;
24796                constraints.push(DomainConstraint {
24797                    name: None,
24798                    check: check_expr,
24799                });
24800            } else if self.match_keywords(&[TokenType::Not, TokenType::Null]) {
24801                // NOT NULL is a constraint - represented as VALUE IS NOT NULL
24802                constraints.push(DomainConstraint {
24803                    name: None,
24804                    check: Expression::IsNull(Box::new(IsNull {
24805                        this: Expression::Identifier(Identifier::new("VALUE")),
24806                        not: true,
24807                        postfix_form: false,
24808                    })),
24809                });
24810            } else {
24811                break;
24812            }
24813        }
24814
24815        Ok(Expression::CreateType(Box::new(CreateType {
24816            name,
24817            definition: TypeDefinition::Domain {
24818                base_type,
24819                default,
24820                constraints,
24821            },
24822            if_not_exists,
24823        })))
24824    }
24825
24826    /// Parse CREATE STAGE statement (Snowflake)
24827    fn parse_create_stage(&mut self, or_replace: bool, temporary: bool) -> Result<Expression> {
24828        self.skip(); // consume STAGE (identifier)
24829                     // Parse remaining tokens, normalizing FILE_FORMAT clause
24830        let start = self.current;
24831        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24832            self.skip();
24833        }
24834        let sql = self.tokens_to_sql_stage_format(start, self.current);
24835
24836        // Build the CREATE prefix with modifiers
24837        let mut prefix = String::from("CREATE");
24838        if or_replace {
24839            prefix.push_str(" OR REPLACE");
24840        }
24841        if temporary {
24842            prefix.push_str(" TEMPORARY");
24843        }
24844        prefix.push_str(" STAGE");
24845
24846        Ok(Expression::Raw(Raw {
24847            sql: format!("{} {}", prefix, sql),
24848        }))
24849    }
24850
24851    /// Parse CREATE TAG statement (Snowflake)
24852    fn parse_create_tag(&mut self, or_replace: bool) -> Result<Expression> {
24853        self.skip(); // consume TAG
24854                     // Capture remaining tokens as raw SQL
24855        let start = self.current;
24856        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24857            self.skip();
24858        }
24859        let sql = self.tokens_to_sql(start, self.current);
24860        let prefix = if or_replace {
24861            "CREATE OR REPLACE TAG"
24862        } else {
24863            "CREATE TAG"
24864        };
24865        Ok(Expression::Raw(Raw {
24866            sql: format!("{} {}", prefix, sql),
24867        }))
24868    }
24869
24870    /// Parse CREATE STREAM statement (Snowflake)
24871    fn parse_create_stream(&mut self, _or_replace: bool) -> Result<Expression> {
24872        self.skip(); // consume STREAM
24873                     // Capture remaining tokens as raw SQL
24874        let start = self.current;
24875        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24876            self.skip();
24877        }
24878        let sql = self.tokens_to_sql(start, self.current);
24879        Ok(Expression::Raw(Raw {
24880            sql: format!("CREATE STREAM {}", sql),
24881        }))
24882    }
24883
24884    /// Parse CREATE TASK statement (Snowflake)
24885    /// CREATE [OR REPLACE] TASK [IF NOT EXISTS] name
24886    ///   [WAREHOUSE = wh] [SCHEDULE = '...'] [AFTER task1, ...] [WHEN expr]
24887    ///   AS sql_statement
24888    fn parse_create_task(&mut self, or_replace: bool) -> Result<Expression> {
24889        self.skip(); // consume TASK
24890
24891        let if_not_exists =
24892            self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
24893
24894        // Parse task name (possibly qualified: db.schema.task)
24895        let mut name = String::new();
24896        if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token() {
24897            name.push_str(&self.advance().text);
24898        }
24899        while self.check(TokenType::Dot) {
24900            self.skip();
24901            name.push('.');
24902            if self.check(TokenType::Var) || self.check_keyword() || self.is_identifier_token() {
24903                name.push_str(&self.advance().text);
24904            }
24905        }
24906
24907        // Capture properties as raw text until AS keyword
24908        let props_start = self.current;
24909        while !self.is_at_end() && !self.check(TokenType::Semicolon) && !self.check(TokenType::As) {
24910            self.skip();
24911        }
24912        let properties = self.tokens_to_sql(props_start, self.current);
24913
24914        // Expect AS keyword followed by the SQL body
24915        if !self.match_token(TokenType::As) {
24916            return Err(self.parse_error("Expected AS keyword in CREATE TASK"));
24917        }
24918
24919        let body = self.parse_statement()?;
24920
24921        Ok(Expression::CreateTask(Box::new(
24922            crate::expressions::CreateTask {
24923                or_replace,
24924                if_not_exists,
24925                name,
24926                properties,
24927                body,
24928            },
24929        )))
24930    }
24931
24932    /// Parse CREATE FILE FORMAT statement (Snowflake)
24933    fn parse_create_file_format(
24934        &mut self,
24935        or_replace: bool,
24936        temporary: bool,
24937    ) -> Result<Expression> {
24938        self.skip(); // consume FILE
24939        self.skip(); // consume FORMAT
24940                     // Capture remaining tokens as raw SQL
24941        let start = self.current;
24942        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
24943            self.skip();
24944        }
24945        let sql = self.tokens_to_sql(start, self.current);
24946        let mut prefix = String::from("CREATE");
24947        if or_replace {
24948            prefix.push_str(" OR REPLACE");
24949        }
24950        if temporary {
24951            prefix.push_str(" TEMPORARY");
24952        }
24953        prefix.push_str(" FILE FORMAT ");
24954        prefix.push_str(&sql);
24955        Ok(Expression::Raw(Raw { sql: prefix }))
24956    }
24957
24958    /// Parse DROP TYPE statement
24959    fn parse_drop_type(&mut self) -> Result<Expression> {
24960        self.expect(TokenType::Type)?;
24961
24962        let if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
24963        let name = self.parse_table_ref()?;
24964
24965        let cascade = self.match_token(TokenType::Cascade);
24966        if !cascade {
24967            self.match_token(TokenType::Restrict);
24968        }
24969
24970        Ok(Expression::DropType(Box::new(DropType {
24971            name,
24972            if_exists,
24973            cascade,
24974        })))
24975    }
24976
24977    fn parse_alter_view_with_modifiers(
24978        &mut self,
24979        algorithm: Option<String>,
24980        definer: Option<String>,
24981        sql_security: Option<String>,
24982    ) -> Result<Expression> {
24983        self.expect(TokenType::View)?;
24984
24985        let name = self.parse_table_ref()?;
24986        let mut actions = Vec::new();
24987
24988        // Hive: Optional column aliases with optional COMMENT: (c1, c2) or (c1 COMMENT 'text', c2)
24989        // Only parse if we see LParen followed by identifier (not SELECT for subquery)
24990        let columns = if self.check(TokenType::LParen) {
24991            // Peek ahead to see if this looks like column aliases
24992            let saved = self.current;
24993            self.skip(); // consume LParen
24994
24995            // Check if this is an identifier (column name) vs SELECT keyword
24996            let is_column_aliases = self.check(TokenType::Identifier)
24997                || self.check(TokenType::Var)
24998                || self.check(TokenType::QuotedIdentifier);
24999
25000            if is_column_aliases {
25001                // Parse column aliases
25002                let mut cols = Vec::new();
25003                loop {
25004                    let col_name = self.expect_identifier()?;
25005                    // Optional COMMENT 'text'
25006                    let comment = if self.match_token(TokenType::Comment) {
25007                        Some(self.expect_string()?)
25008                    } else {
25009                        None
25010                    };
25011                    cols.push(ViewColumn {
25012                        name: Identifier::new(col_name),
25013                        comment,
25014                        options: Vec::new(),
25015                    });
25016                    if !self.match_token(TokenType::Comma) {
25017                        break;
25018                    }
25019                }
25020                self.expect(TokenType::RParen)?;
25021                cols
25022            } else {
25023                self.current = saved; // retreat
25024                Vec::new()
25025            }
25026        } else {
25027            Vec::new()
25028        };
25029
25030        // TSQL: WITH option (SCHEMABINDING, ENCRYPTION, VIEW_METADATA) before AS
25031        let with_option = if self.match_token(TokenType::With) {
25032            let opt = self.expect_identifier_or_keyword()?;
25033            Some(opt.to_ascii_uppercase())
25034        } else {
25035            None
25036        };
25037
25038        // Parse actions
25039        if self.match_token(TokenType::Rename) {
25040            self.expect(TokenType::To)?;
25041            actions.push(AlterViewAction::Rename(self.parse_table_ref()?));
25042        } else if self.match_identifier("OWNER") {
25043            self.expect(TokenType::To)?;
25044            actions.push(AlterViewAction::OwnerTo(Identifier::new(
25045                self.expect_identifier()?,
25046            )));
25047        } else if self.match_token(TokenType::Set) {
25048            // Hive: SET TBLPROPERTIES ('key'='value', ...) or SET SCHEMA name
25049            // Trino: SET AUTHORIZATION [ROLE] user
25050            if self.match_identifier("TBLPROPERTIES") {
25051                let props = self.parse_tblproperties_key_value_list()?;
25052                actions.push(AlterViewAction::SetTblproperties(props));
25053            } else if self.match_token(TokenType::Authorization) {
25054                let mut auth_text = String::new();
25055                if self.match_texts(&["ROLE"]) {
25056                    auth_text.push_str("ROLE ");
25057                }
25058                let user = self.expect_identifier()?;
25059                auth_text.push_str(&user);
25060                actions.push(AlterViewAction::SetAuthorization(auth_text));
25061            } else {
25062                self.expect(TokenType::Schema)?;
25063                actions.push(AlterViewAction::SetSchema(Identifier::new(
25064                    self.expect_identifier()?,
25065                )));
25066            }
25067        } else if self.match_identifier("UNSET") {
25068            // Hive: UNSET TBLPROPERTIES ('key1', 'key2', ...)
25069            if !self.match_identifier("TBLPROPERTIES") {
25070                return Err(self.parse_error("Expected TBLPROPERTIES after UNSET"));
25071            }
25072            let keys = self.parse_tblproperties_key_list()?;
25073            actions.push(AlterViewAction::UnsetTblproperties(keys));
25074        } else if self.match_token(TokenType::Alter) {
25075            self.match_token(TokenType::Column);
25076            let col_name = Identifier::new(self.expect_identifier()?);
25077            let action = self.parse_alter_column_action()?;
25078            actions.push(AlterViewAction::AlterColumn {
25079                name: col_name,
25080                action,
25081            });
25082        } else if self.match_token(TokenType::As) {
25083            // AS SELECT ... or AS SELECT ... UNION ... (redefine view query)
25084            let query = self.parse_statement()?;
25085            actions.push(AlterViewAction::AsSelect(Box::new(query)));
25086        }
25087
25088        Ok(Expression::AlterView(Box::new(AlterView {
25089            name,
25090            actions,
25091            algorithm,
25092            definer,
25093            sql_security,
25094            with_option,
25095            columns,
25096        })))
25097    }
25098
25099    /// Parse TBLPROPERTIES key-value list: ('key1'='value1', 'key2'='value2', ...)
25100    fn parse_tblproperties_key_value_list(&mut self) -> Result<Vec<(String, String)>> {
25101        self.expect(TokenType::LParen)?;
25102        let mut props = Vec::new();
25103        loop {
25104            let key = self.expect_string()?;
25105            self.expect(TokenType::Eq)?;
25106            let value = self.expect_string()?;
25107            props.push((key, value));
25108            if !self.match_token(TokenType::Comma) {
25109                break;
25110            }
25111        }
25112        self.expect(TokenType::RParen)?;
25113        Ok(props)
25114    }
25115
25116    /// Parse TBLPROPERTIES key list (for UNSET): ('key1', 'key2', ...)
25117    fn parse_tblproperties_key_list(&mut self) -> Result<Vec<String>> {
25118        self.expect(TokenType::LParen)?;
25119        let mut keys = Vec::new();
25120        loop {
25121            let key = self.expect_string()?;
25122            keys.push(key);
25123            if !self.match_token(TokenType::Comma) {
25124                break;
25125            }
25126        }
25127        self.expect(TokenType::RParen)?;
25128        Ok(keys)
25129    }
25130
25131    /// Parse ALTER INDEX statement
25132    fn parse_alter_index(&mut self) -> Result<Expression> {
25133        self.expect(TokenType::Index)?;
25134
25135        // Use expect_identifier_or_keyword_with_quoted to preserve quoted flag
25136        let name = self.expect_identifier_or_keyword_with_quoted()?;
25137
25138        let table = if self.match_token(TokenType::On) {
25139            Some(self.parse_table_ref()?)
25140        } else {
25141            None
25142        };
25143
25144        let mut actions = Vec::new();
25145
25146        // Parse actions
25147        if self.match_token(TokenType::Rename) {
25148            self.expect(TokenType::To)?;
25149            // Also preserve quoted flag for the new name
25150            actions.push(AlterIndexAction::Rename(
25151                self.expect_identifier_or_keyword_with_quoted()?,
25152            ));
25153        } else if self.match_token(TokenType::Set) {
25154            self.match_identifier("TABLESPACE");
25155            actions.push(AlterIndexAction::SetTablespace(
25156                self.expect_identifier_or_keyword_with_quoted()?,
25157            ));
25158        } else if self.match_identifier("VISIBLE") {
25159            actions.push(AlterIndexAction::Visible(true));
25160        } else if self.match_identifier("INVISIBLE") {
25161            actions.push(AlterIndexAction::Visible(false));
25162        }
25163
25164        Ok(Expression::AlterIndex(Box::new(AlterIndex {
25165            name,
25166            table,
25167            actions,
25168        })))
25169    }
25170
25171    // ==================== End DDL Parsing ====================
25172
25173    /// Parse an expression (with precedence)
25174    /// Assignment (:=) has lower precedence than OR, matching Python sqlglot's
25175    /// _parse_expression -> _parse_assignment -> _parse_disjunction chain
25176    fn parse_expression(&mut self) -> Result<Expression> {
25177        let mut left = self.parse_or()?;
25178
25179        // Handle := assignment operator (MySQL @var := val, DuckDB named args/settings)
25180        // This has lower precedence than OR
25181        while self.match_token(TokenType::ColonEq) {
25182            let right = self.parse_or()?;
25183            left = Expression::PropertyEQ(Box::new(BinaryOp::new(left, right)));
25184        }
25185
25186        // ClickHouse ternary operator: condition ? true_value : false_value
25187        // Parsed as: CASE WHEN condition THEN true_value ELSE false_value END
25188        if matches!(
25189            self.config.dialect,
25190            Some(crate::dialects::DialectType::ClickHouse)
25191        ) && self.match_token(TokenType::Parameter)
25192        {
25193            if self.check(TokenType::Colon) {
25194                return Err(
25195                    self.parse_error("Expected true expression after ? in ClickHouse ternary")
25196                );
25197            }
25198            let true_value = self.parse_or()?;
25199            let false_value = if self.match_token(TokenType::Colon) {
25200                self.parse_or()?
25201            } else {
25202                Expression::Null(Null)
25203            };
25204            left = Expression::IfFunc(Box::new(IfFunc {
25205                original_name: None,
25206                condition: left,
25207                true_value,
25208                false_value: Some(false_value),
25209                inferred_type: None,
25210            }));
25211        }
25212
25213        // ClickHouse: APPLY(func) column transformer
25214        // e.g., COLUMNS('pattern') APPLY(toString) APPLY(length)
25215        // Also: APPLY func (no parens), APPLY(x -> expr) (lambda)
25216        // Only match APPLY when followed by ( — bare APPLY without ( is treated as an alias
25217        // by the select expression parser (e.g., SELECT col apply -> SELECT col AS apply)
25218        if matches!(
25219            self.config.dialect,
25220            Some(crate::dialects::DialectType::ClickHouse)
25221        ) {
25222            while self.check(TokenType::Apply) && self.check_next(TokenType::LParen) {
25223                self.skip(); // consume APPLY
25224                self.skip(); // consume (
25225                let expr = self.parse_expression()?;
25226                self.expect(TokenType::RParen)?;
25227                left = Expression::Apply(Box::new(crate::expressions::Apply {
25228                    this: Box::new(left),
25229                    expression: Box::new(expr),
25230                }));
25231            }
25232        }
25233
25234        Ok(left)
25235    }
25236
25237    /// Parse OR expressions
25238    fn parse_or(&mut self) -> Result<Expression> {
25239        let mut left = self.parse_xor()?;
25240
25241        while self.check(TokenType::Or)
25242            || (self.dpipe_is_logical_or() && self.check(TokenType::DPipe))
25243        {
25244            let mut all_comments = self.previous_trailing_comments().to_vec();
25245            // Also capture leading comments on the OR token (comments on a separate line before OR)
25246            all_comments.extend_from_slice(self.current_leading_comments());
25247            self.skip(); // consume OR
25248            all_comments.extend_from_slice(self.previous_trailing_comments());
25249            // Clear trailing_comments from left expression to avoid duplication
25250            if !all_comments.is_empty() {
25251                Self::clear_rightmost_trailing_comments(&mut left);
25252            }
25253            // Filter out empty/whitespace-only comments
25254            all_comments.retain(|c| !c.trim().is_empty());
25255            // Split: block comments go before operator, line comments go after
25256            let mut left_comments = Vec::new();
25257            let mut operator_comments = Vec::new();
25258            for comment in all_comments {
25259                if comment.starts_with("/*") {
25260                    left_comments.push(comment);
25261                } else {
25262                    operator_comments.push(comment);
25263                }
25264            }
25265            let mut right = self.parse_xor()?;
25266            // If parse_comparison stored pending leading comments, attach them
25267            if !self.pending_leading_comments.is_empty() {
25268                let pending = std::mem::take(&mut self.pending_leading_comments);
25269                right = Expression::Annotated(Box::new(Annotated {
25270                    this: right,
25271                    trailing_comments: pending,
25272                }));
25273            }
25274            left = Expression::Or(Box::new(BinaryOp {
25275                left,
25276                right,
25277                left_comments,
25278                operator_comments,
25279                trailing_comments: Vec::new(),
25280                inferred_type: None,
25281            }));
25282        }
25283
25284        Ok(Self::maybe_rebalance_boolean_chain(left, false))
25285    }
25286
25287    /// Whether `||` should be parsed as logical OR for the active dialect.
25288    fn dpipe_is_logical_or(&self) -> bool {
25289        matches!(
25290            self.config.dialect,
25291            Some(crate::dialects::DialectType::MySQL | crate::dialects::DialectType::Solr)
25292        )
25293    }
25294
25295    /// Parse XOR expressions (MySQL logical XOR)
25296    fn parse_xor(&mut self) -> Result<Expression> {
25297        let mut left = self.parse_and()?;
25298
25299        while self.match_token(TokenType::Xor) {
25300            let right = self.parse_and()?;
25301            left = Expression::Xor(Box::new(Xor {
25302                this: Some(Box::new(left)),
25303                expression: Some(Box::new(right)),
25304                expressions: Vec::new(),
25305            }));
25306        }
25307
25308        Ok(left)
25309    }
25310
25311    /// Parse AND expressions
25312    fn parse_and(&mut self) -> Result<Expression> {
25313        let mut left = self.parse_not()?;
25314
25315        while self.check(TokenType::And) {
25316            // Capture comments from the token before AND (left operand's last token)
25317            let mut all_comments = self.previous_trailing_comments().to_vec();
25318            // Also capture leading comments on the AND token (comments on a separate line before AND)
25319            all_comments.extend_from_slice(self.current_leading_comments());
25320            self.skip(); // consume AND
25321                         // Also capture any trailing comments on the AND token itself
25322            all_comments.extend_from_slice(self.previous_trailing_comments());
25323            // Clear trailing_comments from left expression to avoid duplication
25324            if !all_comments.is_empty() {
25325                Self::clear_rightmost_trailing_comments(&mut left);
25326            }
25327            // Filter out empty/whitespace-only comments (e.g., bare "--" with no content)
25328            all_comments.retain(|c| !c.trim().is_empty());
25329            // Split comments: block comments (/*...*/) go BEFORE the operator (left_comments),
25330            // line comments (raw text from --) go AFTER the operator (operator_comments).
25331            // This matches Python sqlglot's behavior where inline block comments stay
25332            // in-place and line comments shift to after the operator.
25333            let mut left_comments = Vec::new();
25334            let mut operator_comments = Vec::new();
25335            for comment in all_comments {
25336                if comment.starts_with("/*") {
25337                    left_comments.push(comment);
25338                } else {
25339                    operator_comments.push(comment);
25340                }
25341            }
25342            let mut right = self.parse_not()?;
25343            // If parse_comparison stored pending leading comments (comments before
25344            // the right operand's first token with no comparison following),
25345            // attach them as trailing_comments on the right expression.
25346            if !self.pending_leading_comments.is_empty() {
25347                let pending = std::mem::take(&mut self.pending_leading_comments);
25348                right = Expression::Annotated(Box::new(Annotated {
25349                    this: right,
25350                    trailing_comments: pending,
25351                }));
25352            }
25353            left = Expression::And(Box::new(BinaryOp {
25354                left,
25355                right,
25356                left_comments,
25357                operator_comments,
25358                trailing_comments: Vec::new(),
25359                inferred_type: None,
25360            }));
25361        }
25362
25363        Ok(Self::maybe_rebalance_boolean_chain(left, true))
25364    }
25365
25366    /// Rebalance AND/OR chains into a balanced tree when no connector comments are present.
25367    /// This keeps connector chain depth logarithmic for very large predicates.
25368    fn maybe_rebalance_boolean_chain(expr: Expression, is_and: bool) -> Expression {
25369        if !Self::should_rebalance_boolean_chain(&expr, is_and) {
25370            return expr;
25371        }
25372
25373        let terms = Self::flatten_boolean_terms_owned(expr, is_and);
25374        if terms.len() <= 2 {
25375            return Self::build_balanced_boolean_tree(terms, is_and);
25376        }
25377
25378        Self::build_balanced_boolean_tree(terms, is_and)
25379    }
25380
25381    fn should_rebalance_boolean_chain(expr: &Expression, is_and: bool) -> bool {
25382        let mut leaf_count = 0usize;
25383        let mut stack = vec![expr];
25384
25385        while let Some(node) = stack.pop() {
25386            match (is_and, node) {
25387                (true, Expression::And(op)) => {
25388                    if !op.left_comments.is_empty()
25389                        || !op.operator_comments.is_empty()
25390                        || !op.trailing_comments.is_empty()
25391                    {
25392                        return false;
25393                    }
25394                    stack.push(&op.right);
25395                    stack.push(&op.left);
25396                }
25397                (false, Expression::Or(op)) => {
25398                    if !op.left_comments.is_empty()
25399                        || !op.operator_comments.is_empty()
25400                        || !op.trailing_comments.is_empty()
25401                    {
25402                        return false;
25403                    }
25404                    stack.push(&op.right);
25405                    stack.push(&op.left);
25406                }
25407                _ => leaf_count += 1,
25408            }
25409        }
25410
25411        leaf_count > 2
25412    }
25413
25414    fn flatten_boolean_terms_owned(expr: Expression, is_and: bool) -> Vec<Expression> {
25415        let mut terms = Vec::new();
25416        let mut stack = vec![expr];
25417
25418        while let Some(node) = stack.pop() {
25419            match (is_and, node) {
25420                (true, Expression::And(op)) => {
25421                    stack.push(op.right);
25422                    stack.push(op.left);
25423                }
25424                (false, Expression::Or(op)) => {
25425                    stack.push(op.right);
25426                    stack.push(op.left);
25427                }
25428                (_, other) => terms.push(other),
25429            }
25430        }
25431
25432        terms
25433    }
25434
25435    fn build_balanced_boolean_tree(mut terms: Vec<Expression>, is_and: bool) -> Expression {
25436        if terms.is_empty() {
25437            return Expression::Null(Null);
25438        }
25439
25440        while terms.len() > 1 {
25441            let mut next = Vec::with_capacity((terms.len() + 1) / 2);
25442            let mut iter = terms.into_iter();
25443
25444            while let Some(left) = iter.next() {
25445                if let Some(right) = iter.next() {
25446                    let combined = if is_and {
25447                        Expression::And(Box::new(BinaryOp::new(left, right)))
25448                    } else {
25449                        Expression::Or(Box::new(BinaryOp::new(left, right)))
25450                    };
25451                    next.push(combined);
25452                } else {
25453                    next.push(left);
25454                }
25455            }
25456
25457            terms = next;
25458        }
25459
25460        terms.pop().unwrap_or(Expression::Null(Null))
25461    }
25462
25463    /// Parse NOT expressions
25464    fn parse_not(&mut self) -> Result<Expression> {
25465        if self.match_token(TokenType::Not) {
25466            let expr = self.parse_not()?;
25467            Ok(Expression::Not(Box::new(UnaryOp::new(expr))))
25468        } else {
25469            self.parse_comparison()
25470        }
25471    }
25472
25473    /// Parse comparison expressions
25474    fn parse_comparison(&mut self) -> Result<Expression> {
25475        // Capture leading comments from the first token before parsing the left side.
25476        // If a comparison operator follows, these are placed after the left operand.
25477        let pre_left_comments = self.current_leading_comments().to_vec();
25478        let mut left = self.parse_bitwise_or()?;
25479
25480        // Only attach pre-left comments when a comparison operator follows.
25481        // When no comparison follows (e.g., in SELECT list expressions or AND operands),
25482        // the comments are returned to the caller by being accessible via the
25483        // `comparison_pre_left_comments` field, so they can be placed appropriately
25484        // (e.g., after an alias name, or after the expression in an AND chain).
25485        let has_comparison_op = !self.is_at_end()
25486            && matches!(
25487                self.peek().token_type,
25488                TokenType::Eq
25489                    | TokenType::Neq
25490                    | TokenType::Lt
25491                    | TokenType::Gt
25492                    | TokenType::Lte
25493                    | TokenType::Gte
25494                    | TokenType::Is
25495                    | TokenType::In
25496                    | TokenType::Not
25497                    | TokenType::Between
25498                    | TokenType::Like
25499                    | TokenType::ILike
25500                    | TokenType::RLike
25501                    | TokenType::SimilarTo
25502            );
25503
25504        if !pre_left_comments.is_empty() {
25505            if has_comparison_op {
25506                // Comparison follows: attach comments between left operand and operator
25507                match &mut left {
25508                    Expression::Column(col) => {
25509                        col.trailing_comments.extend(pre_left_comments);
25510                    }
25511                    Expression::Identifier(id) => {
25512                        id.trailing_comments.extend(pre_left_comments);
25513                    }
25514                    _ => {
25515                        left = Expression::Annotated(Box::new(Annotated {
25516                            this: left,
25517                            trailing_comments: pre_left_comments,
25518                        }));
25519                    }
25520                }
25521            } else {
25522                // No comparison operator: store comments for the caller to use.
25523                // Save them as "pending" comments that the caller can retrieve.
25524                self.pending_leading_comments = pre_left_comments;
25525            }
25526        }
25527
25528        loop {
25529            let mut global_in = false;
25530            if matches!(
25531                self.config.dialect,
25532                Some(crate::dialects::DialectType::ClickHouse)
25533            ) && self.check_identifier("GLOBAL")
25534                && (self.check_next(TokenType::Not) || self.check_next(TokenType::In))
25535            {
25536                self.skip();
25537                global_in = true;
25538            }
25539
25540            let expr = if self.match_token(TokenType::Eq) {
25541                // Check for ANY/ALL subquery
25542                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25543                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25544                    self.expect(TokenType::LParen)?;
25545                    let inner = self.parse_statement()?;
25546                    self.expect(TokenType::RParen)?;
25547                    let subquery = if was_any {
25548                        self.maybe_wrap_in_subquery(inner)
25549                    } else {
25550                        inner
25551                    };
25552                    Expression::Any(Box::new(QuantifiedExpr {
25553                        this: left,
25554                        subquery,
25555                        op: Some(QuantifiedOp::Eq),
25556                    }))
25557                } else if self.match_token(TokenType::All) {
25558                    self.expect(TokenType::LParen)?;
25559                    let inner = self.parse_statement()?;
25560                    self.expect(TokenType::RParen)?;
25561                    let subquery = self.maybe_wrap_in_subquery(inner);
25562                    Expression::All(Box::new(QuantifiedExpr {
25563                        this: left,
25564                        subquery,
25565                        op: Some(QuantifiedOp::Eq),
25566                    }))
25567                } else {
25568                    let right = self.parse_bitwise_or()?;
25569                    let trailing_comments = self.previous_trailing_comments().to_vec();
25570                    Expression::Eq(Box::new(BinaryOp {
25571                        left,
25572                        right,
25573                        left_comments: Vec::new(),
25574                        operator_comments: Vec::new(),
25575                        trailing_comments,
25576                        inferred_type: None,
25577                    }))
25578                }
25579            } else if self.match_token(TokenType::Neq) {
25580                // Check for ANY/ALL subquery
25581                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25582                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25583                    self.expect(TokenType::LParen)?;
25584                    let inner = self.parse_statement()?;
25585                    self.expect(TokenType::RParen)?;
25586                    let subquery = if was_any {
25587                        self.maybe_wrap_in_subquery(inner)
25588                    } else {
25589                        inner
25590                    };
25591                    Expression::Any(Box::new(QuantifiedExpr {
25592                        this: left,
25593                        subquery,
25594                        op: Some(QuantifiedOp::Neq),
25595                    }))
25596                } else if self.match_token(TokenType::All) {
25597                    self.expect(TokenType::LParen)?;
25598                    let inner = self.parse_statement()?;
25599                    self.expect(TokenType::RParen)?;
25600                    let subquery = self.maybe_wrap_in_subquery(inner);
25601                    Expression::All(Box::new(QuantifiedExpr {
25602                        this: left,
25603                        subquery,
25604                        op: Some(QuantifiedOp::Neq),
25605                    }))
25606                } else {
25607                    let right = self.parse_bitwise_or()?;
25608                    let trailing_comments = self.previous_trailing_comments().to_vec();
25609                    Expression::Neq(Box::new(BinaryOp {
25610                        left,
25611                        right,
25612                        left_comments: Vec::new(),
25613                        operator_comments: Vec::new(),
25614                        trailing_comments,
25615                        inferred_type: None,
25616                    }))
25617                }
25618            } else if self.match_token(TokenType::Lt) {
25619                // Check for ANY/ALL subquery
25620                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25621                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25622                    self.expect(TokenType::LParen)?;
25623                    let inner = self.parse_statement()?;
25624                    self.expect(TokenType::RParen)?;
25625                    let subquery = if was_any {
25626                        self.maybe_wrap_in_subquery(inner)
25627                    } else {
25628                        inner
25629                    };
25630                    Expression::Any(Box::new(QuantifiedExpr {
25631                        this: left,
25632                        subquery,
25633                        op: Some(QuantifiedOp::Lt),
25634                    }))
25635                } else if self.match_token(TokenType::All) {
25636                    self.expect(TokenType::LParen)?;
25637                    let inner = self.parse_statement()?;
25638                    self.expect(TokenType::RParen)?;
25639                    let subquery = self.maybe_wrap_in_subquery(inner);
25640                    Expression::All(Box::new(QuantifiedExpr {
25641                        this: left,
25642                        subquery,
25643                        op: Some(QuantifiedOp::Lt),
25644                    }))
25645                } else {
25646                    let right = self.parse_bitwise_or()?;
25647                    let trailing_comments = self.previous_trailing_comments().to_vec();
25648                    Expression::Lt(Box::new(BinaryOp {
25649                        left,
25650                        right,
25651                        left_comments: Vec::new(),
25652                        operator_comments: Vec::new(),
25653                        trailing_comments,
25654                        inferred_type: None,
25655                    }))
25656                }
25657            } else if self.match_token(TokenType::Lte) {
25658                // Check for ANY/ALL subquery
25659                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25660                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25661                    self.expect(TokenType::LParen)?;
25662                    let inner = self.parse_statement()?;
25663                    self.expect(TokenType::RParen)?;
25664                    let subquery = if was_any {
25665                        self.maybe_wrap_in_subquery(inner)
25666                    } else {
25667                        inner
25668                    };
25669                    Expression::Any(Box::new(QuantifiedExpr {
25670                        this: left,
25671                        subquery,
25672                        op: Some(QuantifiedOp::Lte),
25673                    }))
25674                } else if self.match_token(TokenType::All) {
25675                    self.expect(TokenType::LParen)?;
25676                    let inner = self.parse_statement()?;
25677                    self.expect(TokenType::RParen)?;
25678                    let subquery = self.maybe_wrap_in_subquery(inner);
25679                    Expression::All(Box::new(QuantifiedExpr {
25680                        this: left,
25681                        subquery,
25682                        op: Some(QuantifiedOp::Lte),
25683                    }))
25684                } else {
25685                    let right = self.parse_bitwise_or()?;
25686                    let trailing_comments = self.previous_trailing_comments().to_vec();
25687                    Expression::Lte(Box::new(BinaryOp {
25688                        left,
25689                        right,
25690                        left_comments: Vec::new(),
25691                        operator_comments: Vec::new(),
25692                        trailing_comments,
25693                        inferred_type: None,
25694                    }))
25695                }
25696            } else if self.match_token(TokenType::Gt) {
25697                // Check for ANY/ALL subquery
25698                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25699                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25700                    self.expect(TokenType::LParen)?;
25701                    let inner = self.parse_statement()?;
25702                    self.expect(TokenType::RParen)?;
25703                    let subquery = if was_any {
25704                        self.maybe_wrap_in_subquery(inner)
25705                    } else {
25706                        inner
25707                    };
25708                    Expression::Any(Box::new(QuantifiedExpr {
25709                        this: left,
25710                        subquery,
25711                        op: Some(QuantifiedOp::Gt),
25712                    }))
25713                } else if self.match_token(TokenType::All) {
25714                    self.expect(TokenType::LParen)?;
25715                    let inner = self.parse_statement()?;
25716                    self.expect(TokenType::RParen)?;
25717                    let subquery = self.maybe_wrap_in_subquery(inner);
25718                    Expression::All(Box::new(QuantifiedExpr {
25719                        this: left,
25720                        subquery,
25721                        op: Some(QuantifiedOp::Gt),
25722                    }))
25723                } else {
25724                    let right = self.parse_bitwise_or()?;
25725                    let trailing_comments = self.previous_trailing_comments().to_vec();
25726                    Expression::Gt(Box::new(BinaryOp {
25727                        left,
25728                        right,
25729                        left_comments: Vec::new(),
25730                        operator_comments: Vec::new(),
25731                        trailing_comments,
25732                        inferred_type: None,
25733                    }))
25734                }
25735            } else if self.match_token(TokenType::Gte) {
25736                // Check for ANY/ALL subquery
25737                if self.match_token(TokenType::Any) || self.match_token(TokenType::Some) {
25738                    let was_any = self.previous_token_type() == Some(TokenType::Any);
25739                    self.expect(TokenType::LParen)?;
25740                    let inner = self.parse_statement()?;
25741                    self.expect(TokenType::RParen)?;
25742                    let subquery = if was_any {
25743                        self.maybe_wrap_in_subquery(inner)
25744                    } else {
25745                        inner
25746                    };
25747                    Expression::Any(Box::new(QuantifiedExpr {
25748                        this: left,
25749                        subquery,
25750                        op: Some(QuantifiedOp::Gte),
25751                    }))
25752                } else if self.match_token(TokenType::All) {
25753                    self.expect(TokenType::LParen)?;
25754                    let inner = self.parse_statement()?;
25755                    self.expect(TokenType::RParen)?;
25756                    let subquery = self.maybe_wrap_in_subquery(inner);
25757                    Expression::All(Box::new(QuantifiedExpr {
25758                        this: left,
25759                        subquery,
25760                        op: Some(QuantifiedOp::Gte),
25761                    }))
25762                } else {
25763                    let right = self.parse_bitwise_or()?;
25764                    let trailing_comments = self.previous_trailing_comments().to_vec();
25765                    Expression::Gte(Box::new(BinaryOp {
25766                        left,
25767                        right,
25768                        left_comments: Vec::new(),
25769                        operator_comments: Vec::new(),
25770                        trailing_comments,
25771                        inferred_type: None,
25772                    }))
25773                }
25774            } else if self.match_token(TokenType::NullsafeEq) {
25775                // <=> (MySQL NULL-safe equality)
25776                let right = self.parse_bitwise_or()?;
25777                let trailing_comments = self.previous_trailing_comments().to_vec();
25778                Expression::NullSafeEq(Box::new(BinaryOp {
25779                    left,
25780                    right,
25781                    left_comments: Vec::new(),
25782                    operator_comments: Vec::new(),
25783                    trailing_comments,
25784                    inferred_type: None,
25785                }))
25786            } else if self.check_identifier("SOUNDS") && self.check_next(TokenType::Like) {
25787                // MySQL SOUNDS LIKE: expr SOUNDS LIKE expr -> SOUNDEX(expr) = SOUNDEX(expr)
25788                self.skip(); // consume SOUNDS
25789                self.skip(); // consume LIKE
25790                let right = self.parse_bitwise_or()?;
25791                // Transform: SOUNDEX(left) = SOUNDEX(right)
25792                let soundex_left = Expression::Function(Box::new(Function::new(
25793                    "SOUNDEX".to_string(),
25794                    vec![left],
25795                )));
25796                let soundex_right = Expression::Function(Box::new(Function::new(
25797                    "SOUNDEX".to_string(),
25798                    vec![right],
25799                )));
25800                Expression::Eq(Box::new(BinaryOp::new(soundex_left, soundex_right)))
25801            } else if self.match_token(TokenType::Like) {
25802                // Check for ANY/ALL/SOME quantifier
25803                let quantifier = if self.match_token(TokenType::Any) {
25804                    Some("ANY".to_string())
25805                } else if self.match_token(TokenType::All) {
25806                    Some("ALL".to_string())
25807                } else if self.match_token(TokenType::Some) {
25808                    Some("SOME".to_string())
25809                } else {
25810                    None
25811                };
25812                let right = self.parse_bitwise_or()?;
25813                let escape = if self.match_token(TokenType::Escape) {
25814                    Some(self.parse_primary()?)
25815                } else {
25816                    None
25817                };
25818                Expression::Like(Box::new(LikeOp {
25819                    left,
25820                    right,
25821                    escape,
25822                    quantifier,
25823                    inferred_type: None,
25824                }))
25825            } else if self.match_token(TokenType::ILike) {
25826                // Check for ANY/ALL/SOME quantifier
25827                let quantifier = if self.match_token(TokenType::Any) {
25828                    Some("ANY".to_string())
25829                } else if self.match_token(TokenType::All) {
25830                    Some("ALL".to_string())
25831                } else if self.match_token(TokenType::Some) {
25832                    Some("SOME".to_string())
25833                } else {
25834                    None
25835                };
25836                let right = self.parse_bitwise_or()?;
25837                let escape = if self.match_token(TokenType::Escape) {
25838                    Some(self.parse_primary()?)
25839                } else {
25840                    None
25841                };
25842                Expression::ILike(Box::new(LikeOp {
25843                    left,
25844                    right,
25845                    escape,
25846                    quantifier,
25847                    inferred_type: None,
25848                }))
25849            } else if self.check_identifier("SIMILAR") && self.check_next(TokenType::To) {
25850                // SIMILAR TO operator (PostgreSQL/Redshift regex-like pattern matching)
25851                self.skip(); // consume SIMILAR
25852                self.skip(); // consume TO
25853                let pattern = self.parse_bitwise_or()?;
25854                let escape = if self.match_token(TokenType::Escape) {
25855                    Some(self.parse_primary()?)
25856                } else {
25857                    None
25858                };
25859                Expression::SimilarTo(Box::new(SimilarToExpr {
25860                    this: left,
25861                    pattern,
25862                    escape,
25863                    not: false,
25864                }))
25865            } else if self.match_token(TokenType::Glob) {
25866                let right = self.parse_bitwise_or()?;
25867                Expression::Glob(Box::new(BinaryOp::new(left, right)))
25868            } else if self.match_token(TokenType::Match) {
25869                // SQLite MATCH operator (FTS full-text search)
25870                let right = self.parse_bitwise_or()?;
25871                Expression::Match(Box::new(BinaryOp::new(left, right)))
25872            } else if self.match_token(TokenType::RLike) || self.match_token(TokenType::Tilde) {
25873                // PostgreSQL ~ (regexp match) operator / RLIKE / REGEXP
25874                let right = self.parse_bitwise_or()?;
25875                Expression::RegexpLike(Box::new(RegexpFunc {
25876                    this: left,
25877                    pattern: right,
25878                    flags: None,
25879                }))
25880            } else if matches!(
25881                self.config.dialect,
25882                Some(crate::dialects::DialectType::Exasol)
25883            ) && self.check_identifier("REGEXP_LIKE")
25884            {
25885                // Exasol: REGEXP_LIKE as infix binary operator
25886                self.skip(); // consume REGEXP_LIKE
25887                let right = self.parse_bitwise_or()?;
25888                Expression::RegexpLike(Box::new(RegexpFunc {
25889                    this: left,
25890                    pattern: right,
25891                    flags: None,
25892                }))
25893            } else if self.match_token(TokenType::IRLike) {
25894                // PostgreSQL ~* (case-insensitive regexp match) operator
25895                let right = self.parse_bitwise_or()?;
25896                Expression::RegexpILike(Box::new(RegexpILike {
25897                    this: Box::new(left),
25898                    expression: Box::new(right),
25899                    flag: None,
25900                }))
25901            } else if self.match_token(TokenType::NotLike) {
25902                // PostgreSQL !~~ (NOT LIKE) operator
25903                let right = self.parse_bitwise_or()?;
25904                let escape = if self.match_token(TokenType::Escape) {
25905                    Some(self.parse_primary()?)
25906                } else {
25907                    None
25908                };
25909                let like_expr = Expression::Like(Box::new(LikeOp {
25910                    left,
25911                    right,
25912                    escape,
25913                    quantifier: None,
25914                    inferred_type: None,
25915                }));
25916                Expression::Not(Box::new(UnaryOp::new(like_expr)))
25917            } else if self.match_token(TokenType::NotILike) {
25918                // PostgreSQL !~~* (NOT ILIKE) operator
25919                let right = self.parse_bitwise_or()?;
25920                let escape = if self.match_token(TokenType::Escape) {
25921                    Some(self.parse_primary()?)
25922                } else {
25923                    None
25924                };
25925                let ilike_expr = Expression::ILike(Box::new(LikeOp {
25926                    left,
25927                    right,
25928                    escape,
25929                    quantifier: None,
25930                    inferred_type: None,
25931                }));
25932                Expression::Not(Box::new(UnaryOp::new(ilike_expr)))
25933            } else if self.match_token(TokenType::NotRLike) {
25934                // PostgreSQL !~ (NOT regexp match) operator
25935                let right = self.parse_bitwise_or()?;
25936                let regexp_expr = Expression::RegexpLike(Box::new(RegexpFunc {
25937                    this: left,
25938                    pattern: right,
25939                    flags: None,
25940                }));
25941                Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
25942            } else if self.match_token(TokenType::NotIRLike) {
25943                // PostgreSQL !~* (NOT case-insensitive regexp match) operator
25944                let right = self.parse_bitwise_or()?;
25945                let regexp_expr = Expression::RegexpILike(Box::new(RegexpILike {
25946                    this: Box::new(left),
25947                    expression: Box::new(right),
25948                    flag: None,
25949                }));
25950                Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
25951            } else if self.check(TokenType::Is)
25952                && !self.is_last_expression_token(TokenType::Is)
25953                && self.match_token(TokenType::Is)
25954            {
25955                let not = self.match_token(TokenType::Not);
25956                if self.match_token(TokenType::Null) {
25957                    let expr = Expression::IsNull(Box::new(IsNull {
25958                        this: left,
25959                        not,
25960                        postfix_form: false,
25961                    }));
25962                    // ClickHouse: IS NULL :: Type — handle :: cast after IS NULL
25963                    if matches!(
25964                        self.config.dialect,
25965                        Some(crate::dialects::DialectType::ClickHouse)
25966                    ) && self.check(TokenType::DColon)
25967                    {
25968                        self.skip(); // consume ::
25969                        let data_type = self.parse_data_type_for_cast()?;
25970                        Expression::Cast(Box::new(Cast {
25971                            this: expr,
25972                            to: data_type,
25973                            trailing_comments: Vec::new(),
25974                            double_colon_syntax: true,
25975                            format: None,
25976                            default: None,
25977                            inferred_type: None,
25978                        }))
25979                    } else {
25980                        expr
25981                    }
25982                } else if self.match_token(TokenType::True) {
25983                    // IS TRUE / IS NOT TRUE
25984                    Expression::IsTrue(Box::new(IsTrueFalse { this: left, not }))
25985                } else if self.match_token(TokenType::False) {
25986                    // IS FALSE / IS NOT FALSE
25987                    Expression::IsFalse(Box::new(IsTrueFalse { this: left, not }))
25988                } else if self.match_token(TokenType::Distinct) {
25989                    // IS DISTINCT FROM / IS NOT DISTINCT FROM
25990                    self.expect(TokenType::From)?;
25991                    let right = self.parse_bitwise_or()?;
25992                    if not {
25993                        // IS NOT DISTINCT FROM → null-safe equality
25994                        Expression::NullSafeEq(Box::new(BinaryOp::new(left, right)))
25995                    } else {
25996                        // IS DISTINCT FROM → null-safe inequality
25997                        Expression::NullSafeNeq(Box::new(BinaryOp::new(left, right)))
25998                    }
25999                } else if self.match_identifier("UNKNOWN") {
26000                    // IS UNKNOWN
26001                    Expression::IsNull(Box::new(IsNull {
26002                        this: left,
26003                        not,
26004                        postfix_form: false,
26005                    }))
26006                } else if self.match_texts(&["JSON"]) {
26007                    // IS JSON [VALUE|SCALAR|OBJECT|ARRAY] [WITH UNIQUE KEYS|WITHOUT UNIQUE KEYS|UNIQUE KEYS]
26008                    let json_type = if self.match_texts(&["VALUE"]) {
26009                        Some("VALUE".to_string())
26010                    } else if self.match_texts(&["SCALAR"]) {
26011                        Some("SCALAR".to_string())
26012                    } else if self.match_texts(&["OBJECT"]) {
26013                        Some("OBJECT".to_string())
26014                    } else if self.match_texts(&["ARRAY"]) {
26015                        Some("ARRAY".to_string())
26016                    } else {
26017                        None
26018                    };
26019
26020                    // Parse optional key uniqueness constraint
26021                    let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE", "KEYS"]) {
26022                        Some(JsonUniqueKeys::With)
26023                    } else if self.match_text_seq(&["WITHOUT", "UNIQUE", "KEYS"]) {
26024                        Some(JsonUniqueKeys::Without)
26025                    } else if self.match_text_seq(&["UNIQUE", "KEYS"]) {
26026                        // Shorthand for WITH UNIQUE KEYS
26027                        Some(JsonUniqueKeys::Shorthand)
26028                    } else {
26029                        None
26030                    };
26031
26032                    Expression::IsJson(Box::new(IsJson {
26033                        this: left,
26034                        json_type,
26035                        unique_keys,
26036                        negated: not,
26037                    }))
26038                } else {
26039                    // IS followed by an expression (e.g., IS ?)
26040                    // If we matched NOT, wrap the IS expression in NOT
26041                    let right = self.parse_primary()?;
26042                    let is_expr = Expression::Is(Box::new(BinaryOp::new(left, right)));
26043                    if not {
26044                        Expression::Not(Box::new(UnaryOp::new(is_expr)))
26045                    } else {
26046                        is_expr
26047                    }
26048                }
26049            } else if self.match_token(TokenType::Not) {
26050                // Handle NOT IN, NOT BETWEEN, NOT LIKE, NOT ILIKE, etc.
26051                if self.match_token(TokenType::In) {
26052                    // BigQuery: NOT IN UNNEST(expr)
26053                    if self.check_identifier("UNNEST") {
26054                        self.skip(); // consume UNNEST
26055                        self.expect(TokenType::LParen)?;
26056                        let unnest_expr = self.parse_expression()?;
26057                        self.expect(TokenType::RParen)?;
26058                        Expression::In(Box::new(In {
26059                            this: left,
26060                            expressions: Vec::new(),
26061                            query: None,
26062                            not: true,
26063                            global: global_in,
26064                            unnest: Some(Box::new(unnest_expr)),
26065                            is_field: false,
26066                        }))
26067                    } else if self.match_token(TokenType::LParen) {
26068                        if self.check(TokenType::Select) || self.check(TokenType::With) {
26069                            let subquery = self.parse_statement()?;
26070                            self.expect(TokenType::RParen)?;
26071                            Expression::In(Box::new(In {
26072                                this: left,
26073                                expressions: Vec::new(),
26074                                query: Some(subquery),
26075                                not: true,
26076                                global: global_in,
26077                                unnest: None,
26078                                is_field: false,
26079                            }))
26080                        } else if self.check(TokenType::RParen) {
26081                            // Empty NOT IN set: NOT IN ()
26082                            self.skip();
26083                            Expression::In(Box::new(In {
26084                                this: left,
26085                                expressions: Vec::new(),
26086                                query: None,
26087                                not: true,
26088                                global: global_in,
26089                                unnest: None,
26090                                is_field: false,
26091                            }))
26092                        } else {
26093                            let expressions = self.parse_expression_list()?;
26094                            self.expect(TokenType::RParen)?;
26095                            Expression::In(Box::new(In {
26096                                this: left,
26097                                expressions,
26098                                query: None,
26099                                not: true,
26100                                global: global_in,
26101                                unnest: None,
26102                                is_field: false,
26103                            }))
26104                        }
26105                    } else {
26106                        // ClickHouse/DuckDB: IN without parentheses: expr NOT IN table_name
26107                        let table_expr = self.parse_primary()?;
26108                        Expression::In(Box::new(In {
26109                            this: left,
26110                            expressions: vec![table_expr],
26111                            query: None,
26112                            not: true,
26113                            global: global_in,
26114                            unnest: None,
26115                            is_field: true,
26116                        }))
26117                    }
26118                } else if self.match_token(TokenType::Between) {
26119                    // Check for SYMMETRIC/ASYMMETRIC qualifier
26120                    let symmetric = if self.match_texts(&["SYMMETRIC"]) {
26121                        Some(true)
26122                    } else if self.match_texts(&["ASYMMETRIC"]) {
26123                        Some(false)
26124                    } else {
26125                        None
26126                    };
26127                    let low = self.parse_bitwise_or()?;
26128                    self.expect(TokenType::And)?;
26129                    let high = self.parse_bitwise_or()?;
26130                    Expression::Between(Box::new(Between {
26131                        this: left,
26132                        low,
26133                        high,
26134                        not: true,
26135                        symmetric,
26136                    }))
26137                } else if self.check_identifier("SOUNDS") && self.check_next(TokenType::Like) {
26138                    // MySQL NOT SOUNDS LIKE: expr NOT SOUNDS LIKE expr -> NOT SOUNDEX(expr) = SOUNDEX(expr)
26139                    self.skip(); // consume SOUNDS
26140                    self.skip(); // consume LIKE
26141                    let right = self.parse_bitwise_or()?;
26142                    let soundex_left = Expression::Function(Box::new(Function::new(
26143                        "SOUNDEX".to_string(),
26144                        vec![left],
26145                    )));
26146                    let soundex_right = Expression::Function(Box::new(Function::new(
26147                        "SOUNDEX".to_string(),
26148                        vec![right],
26149                    )));
26150                    let eq_expr =
26151                        Expression::Eq(Box::new(BinaryOp::new(soundex_left, soundex_right)));
26152                    Expression::Not(Box::new(UnaryOp::new(eq_expr)))
26153                } else if self.match_token(TokenType::Like) {
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 like_expr = Expression::Like(Box::new(LikeOp {
26161                        left,
26162                        right,
26163                        escape,
26164                        quantifier: None,
26165                        inferred_type: None,
26166                    }));
26167                    Expression::Not(Box::new(UnaryOp::new(like_expr)))
26168                } else if self.match_token(TokenType::ILike) {
26169                    let right = self.parse_bitwise_or()?;
26170                    let escape = if self.match_token(TokenType::Escape) {
26171                        Some(self.parse_primary()?)
26172                    } else {
26173                        None
26174                    };
26175                    let ilike_expr = Expression::ILike(Box::new(LikeOp {
26176                        left,
26177                        right,
26178                        escape,
26179                        quantifier: None,
26180                        inferred_type: None,
26181                    }));
26182                    Expression::Not(Box::new(UnaryOp::new(ilike_expr)))
26183                } else if self.check_identifier("SIMILAR") && self.check_next(TokenType::To) {
26184                    // NOT SIMILAR TO
26185                    self.skip(); // consume SIMILAR
26186                    self.skip(); // consume TO
26187                    let pattern = self.parse_bitwise_or()?;
26188                    let escape = if self.match_token(TokenType::Escape) {
26189                        Some(self.parse_primary()?)
26190                    } else {
26191                        None
26192                    };
26193                    Expression::SimilarTo(Box::new(SimilarToExpr {
26194                        this: left,
26195                        pattern,
26196                        escape,
26197                        not: true,
26198                    }))
26199                } else if self.match_token(TokenType::RLike) {
26200                    let right = self.parse_bitwise_or()?;
26201                    let regexp_expr = Expression::RegexpLike(Box::new(RegexpFunc {
26202                        this: left,
26203                        pattern: right,
26204                        flags: None,
26205                    }));
26206                    Expression::Not(Box::new(UnaryOp::new(regexp_expr)))
26207                } else if self.match_token(TokenType::Null) {
26208                    // SQLite: a NOT NULL (postfix form, two separate tokens)
26209                    // Creates NOT(a IS NULL) which is semantically equivalent
26210                    let is_null =
26211                        Expression::Is(Box::new(BinaryOp::new(left, Expression::Null(Null))));
26212                    Expression::Not(Box::new(UnaryOp::new(is_null)))
26213                } else {
26214                    // NOT followed by something else - revert
26215                    return Ok(left);
26216                }
26217            } else if self.match_token(TokenType::In) {
26218                // BigQuery: IN UNNEST(expr)
26219                if self.check_identifier("UNNEST") {
26220                    self.skip(); // consume UNNEST
26221                    self.expect(TokenType::LParen)?;
26222                    let unnest_expr = self.parse_expression()?;
26223                    self.expect(TokenType::RParen)?;
26224                    Expression::In(Box::new(In {
26225                        this: left,
26226                        expressions: Vec::new(),
26227                        query: None,
26228                        not: false,
26229                        global: global_in,
26230                        unnest: Some(Box::new(unnest_expr)),
26231                        is_field: false,
26232                    }))
26233                } else if self.match_token(TokenType::LParen) {
26234                    // Standard IN (list) or IN (subquery)
26235                    // Check if this is a subquery (IN (SELECT ...) or IN (WITH ... SELECT ...))
26236                    if self.check(TokenType::Select) || self.check(TokenType::With) {
26237                        // Use parse_statement to handle both SELECT and WITH...SELECT
26238                        let subquery = self.parse_statement()?;
26239                        self.expect(TokenType::RParen)?;
26240                        Expression::In(Box::new(In {
26241                            this: left,
26242                            expressions: Vec::new(),
26243                            query: Some(subquery),
26244                            not: false,
26245                            global: global_in,
26246                            unnest: None,
26247                            is_field: false,
26248                        }))
26249                    } else if self.check(TokenType::RParen) {
26250                        // Empty IN set: IN ()
26251                        self.skip();
26252                        Expression::In(Box::new(In {
26253                            this: left,
26254                            expressions: Vec::new(),
26255                            query: None,
26256                            not: false,
26257                            global: global_in,
26258                            unnest: None,
26259                            is_field: false,
26260                        }))
26261                    } else {
26262                        let expressions = self.parse_expression_list()?;
26263                        self.expect(TokenType::RParen)?;
26264                        Expression::In(Box::new(In {
26265                            this: left,
26266                            expressions,
26267                            query: None,
26268                            not: false,
26269                            global: global_in,
26270                            unnest: None,
26271                            is_field: false,
26272                        }))
26273                    }
26274                } else {
26275                    // DuckDB: IN without parentheses for array/list membership: 'red' IN tbl.flags
26276                    let expr = self.parse_bitwise_or()?;
26277                    Expression::In(Box::new(In {
26278                        this: left,
26279                        expressions: vec![expr],
26280                        query: None,
26281                        not: false,
26282                        global: global_in,
26283                        unnest: None,
26284                        is_field: true,
26285                    }))
26286                }
26287            } else if self.match_token(TokenType::Between) {
26288                // Check for SYMMETRIC/ASYMMETRIC qualifier
26289                let symmetric = if self.match_texts(&["SYMMETRIC"]) {
26290                    Some(true)
26291                } else if self.match_texts(&["ASYMMETRIC"]) {
26292                    Some(false)
26293                } else {
26294                    None
26295                };
26296                let low = self.parse_bitwise_or()?;
26297                self.expect(TokenType::And)?;
26298                let high = self.parse_bitwise_or()?;
26299                Expression::Between(Box::new(Between {
26300                    this: left,
26301                    low,
26302                    high,
26303                    not: false,
26304                    symmetric,
26305                }))
26306            } else if self.match_token(TokenType::Adjacent) {
26307                let right = self.parse_bitwise_or()?;
26308                Expression::Adjacent(Box::new(BinaryOp::new(left, right)))
26309            } else if self.check(TokenType::Overlaps)
26310                && self.current + 1 < self.tokens.len()
26311                && !matches!(
26312                    self.tokens[self.current + 1].token_type,
26313                    TokenType::Semicolon
26314                        | TokenType::Comma
26315                        | TokenType::From
26316                        | TokenType::Where
26317                        | TokenType::RParen
26318                        | TokenType::As
26319                        | TokenType::Join
26320                        | TokenType::On
26321                        | TokenType::OrderBy
26322                        | TokenType::GroupBy
26323                        | TokenType::Having
26324                        | TokenType::Limit
26325                        | TokenType::Union
26326                        | TokenType::Except
26327                        | TokenType::Intersect
26328                        | TokenType::Eof
26329                )
26330            {
26331                self.skip(); // consume OVERLAPS
26332                let right = self.parse_bitwise_or()?;
26333                Expression::Overlaps(Box::new(OverlapsExpr {
26334                    this: Some(left),
26335                    expression: Some(right),
26336                    left_start: None,
26337                    left_end: None,
26338                    right_start: None,
26339                    right_end: None,
26340                }))
26341            } else if self.match_token(TokenType::IsNull) {
26342                // ISNULL postfix operator (PostgreSQL/SQLite)
26343                Expression::IsNull(Box::new(IsNull {
26344                    this: left,
26345                    not: false,
26346                    postfix_form: true,
26347                }))
26348            } else if self.match_token(TokenType::NotNull) {
26349                // NOTNULL postfix operator (PostgreSQL/SQLite)
26350                Expression::IsNull(Box::new(IsNull {
26351                    this: left,
26352                    not: true,
26353                    postfix_form: true,
26354                }))
26355            } else if self.match_token(TokenType::AtAt) {
26356                // PostgreSQL text search match operator (@@)
26357                let right = self.parse_bitwise_or()?;
26358                Expression::TsMatch(Box::new(BinaryOp::new(left, right)))
26359            } else if self.match_token(TokenType::AtGt) {
26360                // PostgreSQL array contains all operator (@>)
26361                let right = self.parse_bitwise_or()?;
26362                Expression::ArrayContainsAll(Box::new(BinaryOp::new(left, right)))
26363            } else if self.match_token(TokenType::LtAt) {
26364                // PostgreSQL array contained by operator (<@)
26365                let right = self.parse_bitwise_or()?;
26366                Expression::ArrayContainedBy(Box::new(BinaryOp::new(left, right)))
26367            } else if self.match_token(TokenType::DAmp) {
26368                // PostgreSQL array overlaps operator (&&)
26369                let right = self.parse_bitwise_or()?;
26370                Expression::ArrayOverlaps(Box::new(BinaryOp::new(left, right)))
26371            } else if self.match_token(TokenType::QMarkAmp) {
26372                // PostgreSQL JSONB contains all top keys operator (?&)
26373                let right = self.parse_bitwise_or()?;
26374                Expression::JSONBContainsAllTopKeys(Box::new(BinaryOp::new(left, right)))
26375            } else if self.match_token(TokenType::QMarkPipe) {
26376                // PostgreSQL JSONB contains any top key operator (?|)
26377                let right = self.parse_bitwise_or()?;
26378                Expression::JSONBContainsAnyTopKeys(Box::new(BinaryOp::new(left, right)))
26379            } else if !matches!(
26380                self.config.dialect,
26381                Some(crate::dialects::DialectType::ClickHouse)
26382            ) && self.match_token(TokenType::Parameter)
26383            {
26384                // PostgreSQL JSONB contains key operator (?)
26385                // Note: ? is tokenized as Parameter, but when used between expressions
26386                // it's the JSONB key existence operator
26387                // ClickHouse uses ? as ternary operator instead, handled in parse_assignment()
26388                let right = self.parse_bitwise_or()?;
26389                Expression::JSONBContains(Box::new(BinaryFunc {
26390                    original_name: Some("?".to_string()),
26391                    this: left,
26392                    expression: right,
26393                    inferred_type: None,
26394                }))
26395            } else if self.match_token(TokenType::HashDash) {
26396                // PostgreSQL JSONB delete at path operator (#-)
26397                let right = self.parse_bitwise_or()?;
26398                Expression::JSONBDeleteAtPath(Box::new(BinaryOp::new(left, right)))
26399            } else if self.match_token(TokenType::AmpLt) {
26400                // PostgreSQL range extends left operator (&<)
26401                let right = self.parse_bitwise_or()?;
26402                Expression::ExtendsLeft(Box::new(BinaryOp::new(left, right)))
26403            } else if self.match_token(TokenType::AmpGt) {
26404                // PostgreSQL range extends right operator (&>)
26405                let right = self.parse_bitwise_or()?;
26406                Expression::ExtendsRight(Box::new(BinaryOp::new(left, right)))
26407            } else if self.match_identifier("MEMBER") {
26408                // MySQL MEMBER OF(expr) operator - JSON membership test
26409                self.expect(TokenType::Of)?;
26410                self.expect(TokenType::LParen)?;
26411                let right = self.parse_expression()?;
26412                self.expect(TokenType::RParen)?;
26413                Expression::MemberOf(Box::new(BinaryOp::new(left, right)))
26414            } else if self.match_token(TokenType::CaretAt) {
26415                // DuckDB/PostgreSQL starts-with operator (^@)
26416                let right = self.parse_bitwise_or()?;
26417                Expression::StartsWith(Box::new(BinaryFunc {
26418                    original_name: Some("^@".to_string()),
26419                    this: left,
26420                    expression: right,
26421                    inferred_type: None,
26422                }))
26423            } else if self.match_token(TokenType::LrArrow) {
26424                // PostgreSQL distance operator (<->)
26425                let right = self.parse_bitwise_or()?;
26426                Expression::EuclideanDistance(Box::new(EuclideanDistance {
26427                    this: Box::new(left),
26428                    expression: Box::new(right),
26429                }))
26430            } else if self.match_token(TokenType::Operator) {
26431                // PostgreSQL OPERATOR(schema.op) syntax for schema-qualified operators
26432                // Example: col1 OPERATOR(pg_catalog.~) col2
26433                self.expect(TokenType::LParen)?;
26434
26435                // Collect all tokens between parentheses as the operator text
26436                // This can include schema names, dots, and operator symbols like ~
26437                let mut op_text = String::new();
26438                while !self.check(TokenType::RParen) && !self.is_at_end() {
26439                    op_text.push_str(&self.peek().text);
26440                    self.skip();
26441                }
26442                self.expect(TokenType::RParen)?;
26443
26444                // Collect any inline comments (e.g., /* foo */) between OPERATOR() and the RHS
26445                // Try trailing comments of the RParen (previous token) first,
26446                // then leading comments of the next token
26447                let mut comments = if self.current > 0 {
26448                    std::mem::take(&mut self.tokens[self.current - 1].trailing_comments)
26449                } else {
26450                    Vec::new()
26451                };
26452                if comments.is_empty() && !self.is_at_end() {
26453                    comments = std::mem::take(&mut self.tokens[self.current].comments);
26454                }
26455
26456                // Parse the right-hand side expression
26457                let right = self.parse_bitwise_or()?;
26458
26459                Expression::Operator(Box::new(Operator {
26460                    this: Box::new(left),
26461                    operator: Some(Box::new(Expression::Identifier(Identifier::new(op_text)))),
26462                    expression: Box::new(right),
26463                    comments,
26464                }))
26465            } else {
26466                return Ok(left);
26467            };
26468
26469            left = expr;
26470        }
26471    }
26472
26473    /// Parse bitwise OR expressions (|)
26474    fn parse_bitwise_or(&mut self) -> Result<Expression> {
26475        let mut left = self.parse_bitwise_xor()?;
26476
26477        loop {
26478            if self.match_token(TokenType::Pipe) {
26479                let right = self.parse_bitwise_xor()?;
26480                left = Expression::BitwiseOr(Box::new(BinaryOp::new(left, right)));
26481            } else {
26482                return Ok(left);
26483            }
26484        }
26485    }
26486
26487    /// Parse bitwise operators with an existing left expression
26488    /// Used for DuckDB's @ operator when @col is tokenized as a single Var token
26489    /// We already have the column, now need to continue parsing any binary operators
26490    /// Follows the same precedence chain: bitwise -> shift -> addition -> multiplication
26491    fn parse_bitwise_continuation(&mut self, left: Expression) -> Result<Expression> {
26492        // Start from multiplication level since we have a primary expression (col)
26493        // Then work up through addition, shift, bitwise AND/XOR/OR
26494        let mult_result = self.parse_multiplication_continuation(left)?;
26495        let add_result = self.parse_addition_continuation(mult_result)?;
26496        self.parse_bitwise_or_continuation(add_result)
26497    }
26498
26499    /// Parse bitwise OR with an existing left expression
26500    fn parse_bitwise_or_continuation(&mut self, mut left: Expression) -> Result<Expression> {
26501        loop {
26502            if self.match_token(TokenType::Pipe) {
26503                let right = self.parse_bitwise_xor()?;
26504                left = Expression::BitwiseOr(Box::new(BinaryOp::new(left, right)));
26505            } else {
26506                return Ok(left);
26507            }
26508        }
26509    }
26510
26511    /// Parse multiplication/division with an existing left expression
26512    fn parse_multiplication_continuation(&mut self, mut left: Expression) -> Result<Expression> {
26513        loop {
26514            let expr = if self.match_token(TokenType::Star) {
26515                let right = self.parse_power()?;
26516                Expression::Mul(Box::new(BinaryOp::new(left, right)))
26517            } else if self.match_token(TokenType::Slash) {
26518                let right = self.parse_power()?;
26519                Expression::Div(Box::new(BinaryOp::new(left, right)))
26520            } else if self.match_token(TokenType::Percent) {
26521                let right = self.parse_power()?;
26522                Expression::Mod(Box::new(BinaryOp::new(left, right)))
26523            } else if !self.check(TokenType::QuotedIdentifier)
26524                && (self.match_identifier("DIV") || self.match_token(TokenType::Div))
26525            {
26526                // DIV keyword for integer division (Hive/Spark/MySQL/ClickHouse)
26527                // Don't match QuotedIdentifier — `DIV` is an identifier alias, not an operator
26528                // If DIV was matched as a Var (not keyword Div token), verify it's actually
26529                // an operator by checking that a right operand follows. Otherwise it's an alias.
26530                let matched_as_var = self.previous().token_type == TokenType::Var;
26531                if matched_as_var
26532                    && (self.is_at_end()
26533                        || self.check(TokenType::Semicolon)
26534                        || self.check(TokenType::From)
26535                        || self.check(TokenType::Where)
26536                        || self.check(TokenType::Comma)
26537                        || self.check(TokenType::RParen))
26538                {
26539                    // Backtrack: DIV is being used as an alias, not an operator
26540                    self.current -= 1;
26541                    return Ok(left);
26542                }
26543                let right = self.parse_power()?;
26544                Expression::IntDiv(Box::new(crate::expressions::BinaryFunc {
26545                    this: left,
26546                    expression: right,
26547                    original_name: None,
26548                    inferred_type: None,
26549                }))
26550            } else {
26551                return Ok(left);
26552            };
26553            left = expr;
26554        }
26555    }
26556
26557    /// Parse addition/subtraction with an existing left expression
26558    fn parse_addition_continuation(&mut self, mut left: Expression) -> Result<Expression> {
26559        loop {
26560            let left_comments = self.previous_trailing_comments().to_vec();
26561
26562            let expr = if self.match_token(TokenType::Plus) {
26563                let operator_comments = self.previous_trailing_comments().to_vec();
26564                let right = self.parse_at_time_zone()?;
26565                let trailing_comments = self.previous_trailing_comments().to_vec();
26566                Expression::Add(Box::new(BinaryOp {
26567                    left,
26568                    right,
26569                    left_comments,
26570                    operator_comments,
26571                    trailing_comments,
26572                    inferred_type: None,
26573                }))
26574            } else if self.match_token(TokenType::Dash) {
26575                let operator_comments = self.previous_trailing_comments().to_vec();
26576                let right = self.parse_at_time_zone()?;
26577                let trailing_comments = self.previous_trailing_comments().to_vec();
26578                Expression::Sub(Box::new(BinaryOp {
26579                    left,
26580                    right,
26581                    left_comments,
26582                    operator_comments,
26583                    trailing_comments,
26584                    inferred_type: None,
26585                }))
26586            } else if !self.dpipe_is_logical_or() && self.match_token(TokenType::DPipe) {
26587                let operator_comments = self.previous_trailing_comments().to_vec();
26588                let right = self.parse_at_time_zone()?;
26589                let trailing_comments = self.previous_trailing_comments().to_vec();
26590                Expression::Concat(Box::new(BinaryOp {
26591                    left,
26592                    right,
26593                    left_comments,
26594                    operator_comments,
26595                    trailing_comments,
26596                    inferred_type: None,
26597                }))
26598            } else if self.match_token(TokenType::DQMark) {
26599                let right = self.parse_at_time_zone()?;
26600                Expression::Coalesce(Box::new(crate::expressions::VarArgFunc {
26601                    expressions: vec![left, right],
26602                    original_name: None,
26603                    inferred_type: None,
26604                }))
26605            } else {
26606                return Ok(left);
26607            };
26608
26609            left = expr;
26610        }
26611    }
26612
26613    /// Parse bitwise XOR expressions (^)
26614    fn parse_bitwise_xor(&mut self) -> Result<Expression> {
26615        let mut left = self.parse_bitwise_and()?;
26616
26617        loop {
26618            // In PostgreSQL, ^ is POWER (handled at parse_power level), and # is BitwiseXor
26619            if matches!(
26620                self.config.dialect,
26621                Some(crate::dialects::DialectType::PostgreSQL)
26622                    | Some(crate::dialects::DialectType::Redshift)
26623            ) {
26624                if self.match_token(TokenType::Hash) {
26625                    let right = self.parse_bitwise_and()?;
26626                    left = Expression::BitwiseXor(Box::new(BinaryOp::new(left, right)));
26627                } else {
26628                    return Ok(left);
26629                }
26630            } else if self.match_token(TokenType::Caret) {
26631                let right = self.parse_bitwise_and()?;
26632                left = Expression::BitwiseXor(Box::new(BinaryOp::new(left, right)));
26633            } else {
26634                return Ok(left);
26635            }
26636        }
26637    }
26638
26639    /// Parse bitwise AND expressions (&)
26640    fn parse_bitwise_and(&mut self) -> Result<Expression> {
26641        let mut left = self.parse_shift()?;
26642
26643        loop {
26644            if self.match_token(TokenType::Amp) {
26645                let right = self.parse_shift()?;
26646                left = Expression::BitwiseAnd(Box::new(BinaryOp::new(left, right)));
26647            } else {
26648                return Ok(left);
26649            }
26650        }
26651    }
26652
26653    /// Parse shift expressions (<< and >>)
26654    fn parse_shift(&mut self) -> Result<Expression> {
26655        let mut left = self.parse_addition()?;
26656
26657        loop {
26658            if self.match_token(TokenType::LtLt) {
26659                let right = self.parse_addition()?;
26660                left = Expression::BitwiseLeftShift(Box::new(BinaryOp::new(left, right)));
26661            } else if self.match_token(TokenType::GtGt) {
26662                let right = self.parse_addition()?;
26663                left = Expression::BitwiseRightShift(Box::new(BinaryOp::new(left, right)));
26664            } else {
26665                return Ok(left);
26666            }
26667        }
26668    }
26669
26670    /// Parse addition/subtraction
26671    fn parse_addition(&mut self) -> Result<Expression> {
26672        let mut left = self.parse_at_time_zone()?;
26673
26674        loop {
26675            // Capture comments after left operand before consuming operator
26676            let left_comments = self.previous_trailing_comments().to_vec();
26677
26678            let expr = if self.match_token(TokenType::Plus) {
26679                // Capture comments after operator (before right operand)
26680                let operator_comments = self.previous_trailing_comments().to_vec();
26681                let right = self.parse_at_time_zone()?;
26682                let trailing_comments = self.previous_trailing_comments().to_vec();
26683                Expression::Add(Box::new(BinaryOp {
26684                    left,
26685                    right,
26686                    left_comments,
26687                    operator_comments,
26688                    trailing_comments,
26689                    inferred_type: None,
26690                }))
26691            } else if self.match_token(TokenType::Dash) {
26692                let operator_comments = self.previous_trailing_comments().to_vec();
26693                let right = self.parse_at_time_zone()?;
26694                let trailing_comments = self.previous_trailing_comments().to_vec();
26695                Expression::Sub(Box::new(BinaryOp {
26696                    left,
26697                    right,
26698                    left_comments,
26699                    operator_comments,
26700                    trailing_comments,
26701                    inferred_type: None,
26702                }))
26703            } else if !self.dpipe_is_logical_or() && self.match_token(TokenType::DPipe) {
26704                let operator_comments = self.previous_trailing_comments().to_vec();
26705                let right = self.parse_at_time_zone()?;
26706                let trailing_comments = self.previous_trailing_comments().to_vec();
26707                Expression::Concat(Box::new(BinaryOp {
26708                    left,
26709                    right,
26710                    left_comments,
26711                    operator_comments,
26712                    trailing_comments,
26713                    inferred_type: None,
26714                }))
26715            } else if self.match_token(TokenType::DQMark) {
26716                let right = self.parse_at_time_zone()?;
26717                Expression::Coalesce(Box::new(crate::expressions::VarArgFunc {
26718                    expressions: vec![left, right],
26719                    original_name: None,
26720                    inferred_type: None,
26721                }))
26722            } else {
26723                return Ok(left);
26724            };
26725
26726            left = expr;
26727        }
26728    }
26729
26730    /// Parse AT TIME ZONE expression
26731    fn parse_at_time_zone(&mut self) -> Result<Expression> {
26732        let mut expr = self.parse_multiplication()?;
26733
26734        // Check for AT TIME ZONE (can be chained)
26735        while self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("AT") {
26736            self.skip(); // consume AT
26737                         // Check for TIME ZONE
26738            if self.check(TokenType::Time) {
26739                self.skip(); // consume TIME
26740                if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("ZONE") {
26741                    self.skip(); // consume ZONE
26742                    let zone = self.parse_unary()?;
26743                    expr = Expression::AtTimeZone(Box::new(AtTimeZone { this: expr, zone }));
26744                } else {
26745                    return Err(self.parse_error("Expected ZONE after AT TIME"));
26746                }
26747            } else {
26748                return Err(self.parse_error("Expected TIME after AT"));
26749            }
26750        }
26751
26752        Ok(expr)
26753    }
26754
26755    /// Parse multiplication/division
26756    fn parse_multiplication(&mut self) -> Result<Expression> {
26757        let mut left = self.parse_power()?;
26758
26759        loop {
26760            let expr = if self.match_token(TokenType::Star) {
26761                let right = self.parse_power()?;
26762                Expression::Mul(Box::new(BinaryOp::new(left, right)))
26763            } else if self.match_token(TokenType::Slash) {
26764                let right = self.parse_power()?;
26765                Expression::Div(Box::new(BinaryOp::new(left, right)))
26766            } else if self.match_token(TokenType::Percent) {
26767                let right = self.parse_power()?;
26768                Expression::Mod(Box::new(BinaryOp::new(left, right)))
26769            } else if !self.check(TokenType::QuotedIdentifier)
26770                && (self.match_identifier("MOD") || self.match_token(TokenType::Mod))
26771            {
26772                // MySQL/Teradata: x MOD y (infix modulo operator)
26773                // Don't match QuotedIdentifier — `MOD` is an identifier alias, not an operator
26774                let right = self.parse_power()?;
26775                Expression::Mod(Box::new(BinaryOp::new(left, right)))
26776            } else if !self.check(TokenType::QuotedIdentifier)
26777                && (self.match_identifier("DIV") || self.match_token(TokenType::Div))
26778            {
26779                // DIV keyword for integer division (Hive/Spark/MySQL/ClickHouse)
26780                // Don't match QuotedIdentifier — `DIV` is an identifier alias, not an operator
26781                // If DIV was matched as a Var (not keyword Div token), verify it's actually
26782                // an operator by checking that a right operand follows. Otherwise it's an alias.
26783                let matched_as_var = self.previous().token_type == TokenType::Var;
26784                if matched_as_var
26785                    && (self.is_at_end()
26786                        || self.check(TokenType::Semicolon)
26787                        || self.check(TokenType::From)
26788                        || self.check(TokenType::Where)
26789                        || self.check(TokenType::Comma)
26790                        || self.check(TokenType::RParen))
26791                {
26792                    // Backtrack: DIV is being used as an alias, not an operator
26793                    self.current -= 1;
26794                    return Ok(left);
26795                }
26796                let right = self.parse_power()?;
26797                Expression::IntDiv(Box::new(crate::expressions::BinaryFunc {
26798                    this: left,
26799                    expression: right,
26800                    original_name: None,
26801                    inferred_type: None,
26802                }))
26803            } else {
26804                return Ok(left);
26805            };
26806
26807            left = expr;
26808        }
26809    }
26810
26811    /// Parse power/exponentiation (**) operator
26812    /// In PostgreSQL/Redshift, ^ (Caret) is POWER, not BitwiseXor
26813    fn parse_power(&mut self) -> Result<Expression> {
26814        let mut left = self.parse_unary()?;
26815
26816        loop {
26817            if self.match_token(TokenType::DStar) {
26818                let right = self.parse_unary()?;
26819                left = Expression::Power(Box::new(BinaryFunc {
26820                    original_name: Some("**".to_string()),
26821                    this: left,
26822                    expression: right,
26823                    inferred_type: None,
26824                }));
26825            } else if matches!(
26826                self.config.dialect,
26827                Some(crate::dialects::DialectType::PostgreSQL)
26828                    | Some(crate::dialects::DialectType::Redshift)
26829                    | Some(crate::dialects::DialectType::DuckDB)
26830            ) && self.match_token(TokenType::Caret)
26831            {
26832                let right = self.parse_unary()?;
26833                left = Expression::Power(Box::new(BinaryFunc {
26834                    original_name: None,
26835                    this: left,
26836                    expression: right,
26837                    inferred_type: None,
26838                }));
26839            } else {
26840                return Ok(left);
26841            }
26842        }
26843    }
26844
26845    /// Try to parse a type literal expression like: point '(4,4)', timestamp '2024-01-01'
26846    /// PostgreSQL allows type name followed by string literal as a cast shorthand.
26847    /// Returns None if not a type literal pattern, so caller can fall through to parse_primary.
26848    fn try_parse_type_literal(&mut self) -> Result<Option<Expression>> {
26849        // Save position for backtracking
26850        let start_pos = self.current;
26851
26852        // Check if we're at an identifier or Var token that could be a type name
26853        if !self.check(TokenType::Identifier) && !self.check(TokenType::Var) {
26854            return Ok(None);
26855        }
26856
26857        // Get the potential type name without consuming
26858        let type_name = self.peek().text.to_ascii_uppercase();
26859
26860        // Check if this looks like a known data type that supports literal syntax
26861        // These are types where PostgreSQL allows TYPE 'value' syntax
26862        // NOTE: DATE, TIME, TIMESTAMP, INTERVAL are NOT here because they have their own
26863        // token types and are handled specially in parse_primary
26864        let is_type_literal_type = matches!(
26865            type_name.as_str(),
26866            // Geometric types (PostgreSQL)
26867            "POINT" | "LINE" | "LSEG" | "BOX" | "PATH" | "POLYGON" | "CIRCLE" |
26868            // Network types (PostgreSQL)
26869            "INET" | "CIDR" | "MACADDR" | "MACADDR8" |
26870            // Other types that support literal syntax
26871            "UUID" | "JSON" | "JSONB" | "XML" | "BIT" | "VARBIT" |
26872            // Range types (PostgreSQL)
26873            "INT4RANGE" | "INT8RANGE" | "NUMRANGE" | "TSRANGE" | "TSTZRANGE" | "DATERANGE"
26874        );
26875
26876        if !is_type_literal_type {
26877            return Ok(None);
26878        }
26879
26880        // Check if the next token (after type name) is a string literal
26881        if self.current + 1 >= self.tokens.len() {
26882            return Ok(None);
26883        }
26884
26885        if self.tokens[self.current + 1].token_type != TokenType::String {
26886            return Ok(None);
26887        }
26888
26889        // This looks like a type literal! Parse it.
26890        // Consume the type name
26891        self.skip();
26892
26893        // Try to parse the data type from the name
26894        let data_type = match self.parse_data_type_from_name(&type_name) {
26895            Ok(dt) => dt,
26896            Err(_) => {
26897                // If we can't parse the type, backtrack
26898                self.current = start_pos;
26899                return Ok(None);
26900            }
26901        };
26902
26903        // Parse the string literal
26904        if !self.check(TokenType::String) {
26905            // Backtrack - something went wrong
26906            self.current = start_pos;
26907            return Ok(None);
26908        }
26909
26910        let string_token = self.advance();
26911        let value = Expression::Literal(Box::new(Literal::String(string_token.text.clone())));
26912
26913        // JSON literal: JSON '"foo"' -> ParseJson expression (matches Python sqlglot)
26914        if matches!(data_type, DataType::Json | DataType::JsonB)
26915            || matches!(type_name.as_str(), "JSON" | "JSONB")
26916        {
26917            return Ok(Some(Expression::ParseJson(Box::new(UnaryFunc {
26918                this: value,
26919                original_name: None,
26920                inferred_type: None,
26921            }))));
26922        }
26923
26924        // Create the Cast expression
26925        Ok(Some(Expression::Cast(Box::new(Cast {
26926            this: value,
26927            to: data_type,
26928            trailing_comments: Vec::new(),
26929            double_colon_syntax: false,
26930            format: None,
26931            default: None,
26932            inferred_type: None,
26933        }))))
26934    }
26935
26936    /// Try to parse type shorthand CAST: INT 1, VARCHAR 'x', STRING 'x', TEXT 'y', etc.
26937    /// In generic mode (no dialect), a type keyword followed by a literal becomes CAST(literal AS type).
26938    /// This matches Python sqlglot's `_parse_types()` behavior.
26939    fn try_parse_type_shorthand_cast(&mut self) -> Result<Option<Expression>> {
26940        // Only apply in generic mode
26941        let is_generic = self.config.dialect.is_none()
26942            || matches!(
26943                self.config.dialect,
26944                Some(crate::dialects::DialectType::Generic)
26945            );
26946        if !is_generic {
26947            return Ok(None);
26948        }
26949
26950        let start_pos = self.current;
26951
26952        // Check if current token is a type keyword
26953        if !self.is_type_keyword() {
26954            return Ok(None);
26955        }
26956
26957        // Don't apply if the type keyword is followed by a left paren (function call)
26958        // or is not followed by a literal
26959        if self.current + 1 >= self.tokens.len() {
26960            return Ok(None);
26961        }
26962
26963        let next_type = self.tokens[self.current + 1].token_type;
26964        // The value after the type keyword must be a literal (number or string)
26965        if !matches!(next_type, TokenType::Number | TokenType::String) {
26966            return Ok(None);
26967        }
26968
26969        // Get the type name
26970        let type_token = self.advance();
26971        let type_name = type_token.text.to_ascii_uppercase();
26972
26973        // Parse the data type
26974        let data_type = match type_name.as_str() {
26975            "INT" | "INTEGER" => DataType::Int {
26976                length: None,
26977                integer_spelling: type_name == "INTEGER",
26978            },
26979            "BIGINT" => DataType::BigInt { length: None },
26980            "SMALLINT" => DataType::SmallInt { length: None },
26981            "TINYINT" => DataType::TinyInt { length: None },
26982            "FLOAT" => DataType::Float {
26983                precision: None,
26984                scale: None,
26985                real_spelling: false,
26986            },
26987            "DOUBLE" => DataType::Double {
26988                precision: None,
26989                scale: None,
26990            },
26991            "DECIMAL" | "NUMERIC" => DataType::Decimal {
26992                precision: None,
26993                scale: None,
26994            },
26995            "REAL" => DataType::Float {
26996                precision: None,
26997                scale: None,
26998                real_spelling: true,
26999            },
27000            "VARCHAR" => DataType::VarChar {
27001                length: None,
27002                parenthesized_length: false,
27003            },
27004            "CHAR" => DataType::Char { length: None },
27005            "TEXT" | "STRING" => DataType::Text,
27006            "BOOLEAN" | "BOOL" => DataType::Boolean,
27007            "BINARY" => DataType::Binary { length: None },
27008            "VARBINARY" => DataType::VarBinary { length: None },
27009            _ => {
27010                // Unknown type, backtrack
27011                self.current = start_pos;
27012                return Ok(None);
27013            }
27014        };
27015
27016        // Parse the literal value
27017        let value = if self.check(TokenType::String) {
27018            let tok = self.advance();
27019            Expression::Literal(Box::new(Literal::String(tok.text.clone())))
27020        } else if self.check(TokenType::Number) {
27021            let tok = self.advance();
27022            Expression::Literal(Box::new(Literal::Number(tok.text.clone())))
27023        } else {
27024            self.current = start_pos;
27025            return Ok(None);
27026        };
27027
27028        // Create the Cast expression
27029        Ok(Some(Expression::Cast(Box::new(Cast {
27030            this: value,
27031            to: data_type,
27032            trailing_comments: Vec::new(),
27033            double_colon_syntax: false,
27034            format: None,
27035            default: None,
27036            inferred_type: None,
27037        }))))
27038    }
27039
27040    /// Parse unary expressions
27041    fn parse_unary(&mut self) -> Result<Expression> {
27042        if self.match_token(TokenType::Plus) {
27043            // Unary plus is a no-op - just parse the inner expression
27044            // This handles +++1 -> 1, +-1 -> -1, etc.
27045            self.parse_unary()
27046        } else if self.match_token(TokenType::Dash) {
27047            let expr = self.parse_unary()?;
27048            Ok(Expression::Neg(Box::new(UnaryOp::new(expr))))
27049        } else if self.match_token(TokenType::Plus) {
27050            // Unary plus: +1, +expr — just return the inner expression (no-op)
27051            self.parse_unary()
27052        } else if self.match_token(TokenType::Tilde) {
27053            let expr = self.parse_unary()?;
27054            Ok(Expression::BitwiseNot(Box::new(UnaryOp::new(expr))))
27055        } else if self.match_token(TokenType::DPipeSlash) {
27056            // ||/ (Cube root - PostgreSQL)
27057            let expr = self.parse_unary()?;
27058            Ok(Expression::Cbrt(Box::new(UnaryFunc::with_name(
27059                expr,
27060                "||/".to_string(),
27061            ))))
27062        } else if self.match_token(TokenType::PipeSlash) {
27063            // |/ (Square root - PostgreSQL)
27064            let expr = self.parse_unary()?;
27065            Ok(Expression::Sqrt(Box::new(UnaryFunc::with_name(
27066                expr,
27067                "|/".to_string(),
27068            ))))
27069        } else if self.check(TokenType::DAt)
27070            && matches!(
27071                self.config.dialect,
27072                Some(crate::dialects::DialectType::DuckDB)
27073            )
27074        {
27075            // DuckDB @ operator: @(-1), @(expr), @-1
27076            // @ is the ABS operator in DuckDB with low precedence
27077            // Python sqlglot: "@": lambda self: exp.Abs(this=self._parse_bitwise())
27078            // This means @col + 1 parses as ABS(col + 1), not ABS(col) + 1
27079            self.skip(); // consume @
27080                         // Parse at bitwise level for correct precedence (matches Python sqlglot)
27081            let expr = self.parse_bitwise_or()?;
27082            Ok(Expression::Abs(Box::new(UnaryFunc::new(expr))))
27083        } else if self.check(TokenType::Var)
27084            && self.peek().text.starts_with('@')
27085            && matches!(
27086                self.config.dialect,
27087                Some(crate::dialects::DialectType::DuckDB)
27088            )
27089        {
27090            // DuckDB @ operator with identifier: @col, @col + 1
27091            // Tokenizer creates "@col" as a single Var token, so we need to handle it here
27092            // Python sqlglot: "@": lambda self: exp.Abs(this=self._parse_bitwise())
27093            let token = self.advance(); // consume @col token
27094            let col_name = &token.text[1..]; // strip leading @
27095
27096            // Create column expression for the identifier part
27097            let col_expr = Expression::boxed_column(Column {
27098                name: Identifier::new(col_name),
27099                table: None,
27100                join_mark: false,
27101                trailing_comments: Vec::new(),
27102                span: None,
27103                inferred_type: None,
27104            });
27105
27106            // Check if followed by operators that should be included in the ABS
27107            // We need to parse any remaining operators at bitwise level
27108            // First, check if there's a binary operator after this column
27109            if self.check(TokenType::Plus)
27110                || self.check(TokenType::Dash)
27111                || self.check(TokenType::Star)
27112                || self.check(TokenType::Slash)
27113                || self.check(TokenType::Percent)
27114                || self.check(TokenType::Amp)
27115                || self.check(TokenType::Pipe)
27116                || self.check(TokenType::Caret)
27117                || self.check(TokenType::LtLt)
27118                || self.check(TokenType::GtGt)
27119            {
27120                // There are more operators - we need to continue parsing at bitwise level
27121                // But parse_bitwise_or expects to start fresh, not continue with existing left
27122                // So we use a helper approach: parse_bitwise_continuation
27123                let full_expr = self.parse_bitwise_continuation(col_expr)?;
27124                Ok(Expression::Abs(Box::new(UnaryFunc::new(full_expr))))
27125            } else {
27126                // Just the column, no more operators
27127                Ok(Expression::Abs(Box::new(UnaryFunc::new(col_expr))))
27128            }
27129        } else if self.check(TokenType::DAt)
27130            && (self.check_next(TokenType::LParen) || self.check_next(TokenType::Dash))
27131        {
27132            // Non-DuckDB dialects: only handle @(expr) and @-expr as ABS
27133            self.skip(); // consume @
27134            let expr = self.parse_bitwise_or()?;
27135            Ok(Expression::Abs(Box::new(UnaryFunc::new(expr))))
27136        } else if self.check(TokenType::Prior)
27137            && !self.check_next(TokenType::As)
27138            && !self.check_next(TokenType::Comma)
27139            && !self.check_next(TokenType::RParen)
27140            && !self.check_next(TokenType::Semicolon)
27141            && self.current + 1 < self.tokens.len()
27142        {
27143            // Oracle PRIOR expression - references parent row's value in hierarchical queries
27144            // Can appear in SELECT list, CONNECT BY, or other expression contexts
27145            // Python sqlglot: "PRIOR": lambda self: self.expression(exp.Prior, this=self._parse_bitwise())
27146            // When followed by AS/comma/rparen/end, treat PRIOR as an identifier (column name)
27147            self.skip(); // consume PRIOR
27148            let expr = self.parse_bitwise_or()?;
27149            Ok(Expression::Prior(Box::new(Prior { this: expr })))
27150        } else {
27151            // Try to parse type literals like: point '(4,4)', timestamp '2024-01-01', interval '1 day'
27152            // PostgreSQL allows type name followed by string literal as a cast shorthand
27153            if let Some(type_literal) = self.try_parse_type_literal()? {
27154                return self.parse_postfix_operators(type_literal);
27155            }
27156            // Try to parse type shorthand CAST: INT 1, VARCHAR 'x', STRING 'x', TEXT 'y', etc.
27157            // In generic mode, type keyword followed by literal -> CAST(literal AS type)
27158            if let Some(type_cast) = self.try_parse_type_shorthand_cast()? {
27159                return self.parse_postfix_operators(type_cast);
27160            }
27161            let expr = self.parse_primary()?;
27162            // Handle postfix exclamation mark for Snowflake model attribute syntax: model!PREDICT(...)
27163            self.parse_postfix_operators(expr)
27164        }
27165    }
27166
27167    /// Parse postfix operators like ! (model attribute in Snowflake) and : (JSON path in Snowflake)
27168    fn parse_postfix_operators(&mut self, mut expr: Expression) -> Result<Expression> {
27169        // Handle Oracle/Redshift outer join marker (+) after column reference
27170        // Syntax: column_ref (+) indicates optional side of join
27171        if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
27172            // Look ahead to verify it's ( + )
27173            let saved_pos = self.current;
27174            if self.match_token(TokenType::LParen)
27175                && self.match_token(TokenType::Plus)
27176                && self.match_token(TokenType::RParen)
27177            {
27178                // Set join_mark on the column expression
27179                if let Expression::Column(ref mut col) = expr {
27180                    col.join_mark = true;
27181                }
27182            } else {
27183                self.current = saved_pos;
27184            }
27185        }
27186
27187        // Handle EXCLAMATION for Snowflake model attribute syntax: model!PREDICT(...)
27188        while self.match_token(TokenType::Exclamation) {
27189            // Parse the attribute/function after the exclamation mark
27190            // This can be either a simple identifier (model!admin) or a function call (model!PREDICT(1))
27191            let attr = self.parse_primary()?;
27192            expr = Expression::ModelAttribute(Box::new(ModelAttribute {
27193                this: Box::new(expr),
27194                expression: Box::new(attr),
27195            }));
27196        }
27197
27198        // Handle COLON for Snowflake JSON path extraction: a:field or a:field.subfield
27199        // This creates JSONExtract expressions that transform to GET_PATH(a, 'field') in Snowflake
27200        expr = self.parse_colon_json_path(expr)?;
27201
27202        // Handle DCOLON (::) - in SingleStore it's JSON extraction, in other dialects it's cast
27203        // SingleStore JSON path syntax:
27204        //   a::b -> JSON_EXTRACT_JSON(a, 'b')
27205        //   a::$b -> JSON_EXTRACT_STRING(a, 'b')
27206        //   a::%b -> JSON_EXTRACT_DOUBLE(a, 'b')
27207        //   a::?names -> JSON match syntax
27208        if matches!(
27209            self.config.dialect,
27210            Some(crate::dialects::DialectType::SingleStore)
27211        ) {
27212            expr = self.parse_singlestore_json_path(expr)?;
27213        } else {
27214            // For other dialects, :: is cast syntax
27215            // IMPORTANT: Use parse_data_type_for_cast to avoid consuming subscripts as array dimensions
27216            // e.g., ::VARIANT[0] should be cast to VARIANT followed by subscript [0]
27217            while self.match_token(TokenType::DColon) {
27218                let data_type = self.parse_data_type_for_cast()?;
27219                expr = Expression::Cast(Box::new(Cast {
27220                    this: expr,
27221                    to: data_type,
27222                    trailing_comments: Vec::new(),
27223                    double_colon_syntax: true,
27224                    format: None,
27225                    default: None,
27226                    inferred_type: None,
27227                }));
27228            }
27229        }
27230
27231        // Teradata: (FORMAT '...') phrase after an expression
27232        if matches!(
27233            self.config.dialect,
27234            Some(crate::dialects::DialectType::Teradata)
27235        ) && self.check(TokenType::LParen)
27236            && self.check_next(TokenType::Format)
27237        {
27238            self.skip(); // consume (
27239            self.skip(); // consume FORMAT
27240            let format = self.expect_string()?;
27241            self.expect(TokenType::RParen)?;
27242            expr = Expression::FormatPhrase(Box::new(FormatPhrase {
27243                this: Box::new(expr),
27244                format,
27245            }));
27246        }
27247
27248        Ok(expr)
27249    }
27250
27251    /// Parse SingleStore JSON path extraction syntax
27252    /// Examples:
27253    ///   a::b -> JSON_EXTRACT_JSON(a, 'b')
27254    ///   a::$b -> JSON_EXTRACT_STRING(a, 'b')
27255    ///   a::%b -> JSON_EXTRACT_DOUBLE(a, 'b')
27256    ///   a::`b`::`2` -> nested JSON extraction
27257    fn parse_singlestore_json_path(&mut self, mut expr: Expression) -> Result<Expression> {
27258        loop {
27259            if self.match_token(TokenType::DColon) {
27260                // :: followed by identifier -> JSON_EXTRACT_JSON
27261                // Check if next is a backtick-quoted identifier or regular identifier
27262                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27263                    self.advance().text
27264                } else if self.check(TokenType::Number) {
27265                    // a::2 -> JSON_EXTRACT_JSON(a, '2')
27266                    self.advance().text
27267                } else {
27268                    return Err(self.parse_error("Expected identifier after ::"));
27269                };
27270
27271                expr = Expression::Function(Box::new(Function::new(
27272                    "JSON_EXTRACT_JSON".to_string(),
27273                    vec![expr, Expression::string(&path_key)],
27274                )));
27275            } else if self.match_token(TokenType::DColonDollar) {
27276                // ::$ followed by identifier -> JSON_EXTRACT_STRING
27277                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27278                    self.advance().text
27279                } else {
27280                    return Err(self.parse_error("Expected identifier after ::$"));
27281                };
27282
27283                expr = Expression::Function(Box::new(Function::new(
27284                    "JSON_EXTRACT_STRING".to_string(),
27285                    vec![expr, Expression::string(&path_key)],
27286                )));
27287            } else if self.match_token(TokenType::DColonPercent) {
27288                // ::% followed by identifier -> JSON_EXTRACT_DOUBLE
27289                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27290                    self.advance().text
27291                } else {
27292                    return Err(self.parse_error("Expected identifier after ::%"));
27293                };
27294
27295                expr = Expression::Function(Box::new(Function::new(
27296                    "JSON_EXTRACT_DOUBLE".to_string(),
27297                    vec![expr, Expression::string(&path_key)],
27298                )));
27299            } else if self.match_token(TokenType::DColonQMark) {
27300                // ::? followed by identifier -> Keep as JSONMatchAny expression for now
27301                let path_key = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
27302                    self.advance().text
27303                } else {
27304                    return Err(self.parse_error("Expected identifier after ::?"));
27305                };
27306
27307                // For now, create a function that will be handled specially
27308                expr = Expression::Function(Box::new(Function::new(
27309                    "JSON_EXTRACT_JSON".to_string(), // placeholder
27310                    vec![expr, Expression::string(&format!("?{}", path_key))],
27311                )));
27312            } else {
27313                break;
27314            }
27315        }
27316        Ok(expr)
27317    }
27318
27319    /// Parse colon-separated JSON path syntax (Snowflake variant extraction)
27320    /// Examples:
27321    ///   a:from -> GET_PATH(a, 'from')
27322    ///   a:b.c.d -> GET_PATH(a, 'b.c.d')
27323    ///   a:from::STRING -> CAST(GET_PATH(a, 'from') AS VARCHAR)
27324    ///   a:b:c.d -> GET_PATH(a, 'b.c.d') (multiple colons joined into single path)
27325    fn parse_colon_json_path(&mut self, mut this: Expression) -> Result<Expression> {
27326        // DuckDB uses colon for prefix alias syntax (e.g., "alias: expr" means "expr AS alias")
27327        // Skip JSON path extraction for DuckDB - it's handled separately in parse_select_expressions
27328        if matches!(
27329            self.config.dialect,
27330            Some(crate::dialects::DialectType::DuckDB)
27331        ) {
27332            return Ok(this);
27333        }
27334
27335        // ClickHouse uses : as part of the ternary operator (condition ? true : false)
27336        // Skip JSON path extraction for ClickHouse to avoid consuming the ternary separator
27337        if matches!(
27338            self.config.dialect,
27339            Some(crate::dialects::DialectType::ClickHouse)
27340        ) {
27341            return Ok(this);
27342        }
27343
27344        // Only apply colon JSON path parsing to identifiers, columns, and function results
27345        // This prevents {'key': 'value'} object literals from being misinterpreted
27346        let is_valid_json_path_base = matches!(
27347            &this,
27348            Expression::Column(_) |
27349            Expression::Identifier(_) |
27350            Expression::Dot(_) |
27351            Expression::JSONExtract(_) |  // Allow chained paths like a:b:c
27352            Expression::Function(_) |     // Allow function results like PARSE_JSON(...):x
27353            Expression::ParseJson(_) |    // Allow PARSE_JSON specifically
27354            Expression::Parameter(_) // Allow positional params like $1:name
27355        );
27356
27357        if !is_valid_json_path_base {
27358            return Ok(this);
27359        }
27360
27361        // Check if we have a colon (but NOT double-colon which is cast syntax)
27362        if !self.check(TokenType::Colon) {
27363            return Ok(this);
27364        }
27365
27366        // Make sure this is not a double-colon (::) which is cast syntax
27367        if self.check_next(TokenType::Colon) {
27368            // This is :: (DColon should have been tokenized, but just in case)
27369            return Ok(this);
27370        }
27371
27372        // Collect ALL the JSON path parts across multiple colons
27373        // a:b.c:d.e -> GET_PATH(a, 'b.c.d.e')
27374        // a:b[0].c -> GET_PATH(a, 'b[0].c')
27375        let mut path_string = String::new();
27376
27377        // Parse all colon-separated path segments
27378        while self.check(TokenType::Colon) && !self.check_next(TokenType::Colon) {
27379            // Save position before consuming colon so we can backtrack
27380            // if what follows isn't a valid JSON path component (e.g., DuckDB's "foo: 1" label syntax)
27381            let saved_pos = self.current;
27382            let saved_path_len = path_string.len();
27383
27384            // Consume the colon
27385            self.skip();
27386
27387            // Parse first path component (required) - can be any identifier including keywords
27388            // Also handle backtick-quoted identifiers like `zip code` or `fb:testid`
27389            // Also handle bracket notation directly after colon: c1:['price'] or c1:["foo bar"]
27390            // IMPORTANT: Check QuotedIdentifier FIRST since is_identifier_token() includes QuotedIdentifier
27391            let mut had_initial_component = false;
27392            if self.check(TokenType::QuotedIdentifier) {
27393                // Quoted field name in variant access
27394                // Snowflake: v:"fruit" → double-quoted key → stored as plain text 'fruit'
27395                // Databricks: raw:`zip code` → backtick-quoted key → stored as bracket notation '["zip code"]'
27396                let quoted_name = self.advance().text.clone();
27397                let is_snowflake = matches!(
27398                    self.config.dialect,
27399                    Some(crate::dialects::DialectType::Snowflake)
27400                );
27401                let needs_bracket = quoted_name.contains(' ') || quoted_name.contains('\'');
27402                if is_snowflake && !needs_bracket {
27403                    // Snowflake double-quoted keys without special chars are stored as plain text
27404                    // Add dot separator for plain segments
27405                    if !path_string.is_empty() {
27406                        path_string.push('.');
27407                    }
27408                    path_string.push_str(&quoted_name);
27409                } else if is_snowflake && needs_bracket {
27410                    // Snowflake keys with spaces/apostrophes use bracket notation: ["key with spaces"]
27411                    // No dot before bracket notation
27412                    path_string.push_str("[\"");
27413                    // Don't escape single quotes here - the generator will handle escaping
27414                    // when outputting the string literal
27415                    path_string.push_str(&quoted_name);
27416                    path_string.push_str("\"]");
27417                } else {
27418                    // Other dialects (Databricks): wrap in bracket notation
27419                    // No dot before bracket notation
27420                    path_string.push_str("[\"");
27421                    for c in quoted_name.chars() {
27422                        if c == '"' {
27423                            path_string.push_str("\\\"");
27424                        } else {
27425                            path_string.push(c);
27426                        }
27427                    }
27428                    path_string.push_str("\"]");
27429                }
27430                had_initial_component = true;
27431            } else if self.is_identifier_token()
27432                || self.is_safe_keyword_as_identifier()
27433                || self.is_reserved_keyword_as_identifier()
27434            {
27435                // Add a dot separator for plain identifier segments
27436                if !path_string.is_empty() {
27437                    path_string.push('.');
27438                }
27439                let first_part = self.advance().text;
27440                path_string.push_str(&first_part);
27441                had_initial_component = true;
27442            } else if self.check(TokenType::LBracket) {
27443                // Bracket notation directly after colon: c1:['price'] or c1:["foo bar"]
27444                // Mark that we have a valid path start - the bracket will be parsed in the loop below
27445                had_initial_component = true;
27446            }
27447
27448            if !had_initial_component {
27449                // Not a valid JSON path component - backtrack and stop
27450                // This handles cases like DuckDB's "foo: 1" label/alias syntax
27451                // where the colon is followed by a non-identifier (e.g., a number)
27452                self.current = saved_pos;
27453                path_string.truncate(saved_path_len);
27454                break;
27455            }
27456
27457            // Parse optional array indices and additional path components
27458            loop {
27459                // Handle array index: [0], [1], [*], ['key'], ["key"], etc.
27460                if self.match_token(TokenType::LBracket) {
27461                    // Parse the index expression (typically a number, identifier, * for wildcard, or string key)
27462                    if self.check(TokenType::Number) {
27463                        path_string.push('[');
27464                        let idx = self.advance().text;
27465                        path_string.push_str(&idx);
27466                        self.expect(TokenType::RBracket)?;
27467                        path_string.push(']');
27468                    } else if self.check(TokenType::Star) {
27469                        // Wildcard array access: [*] matches all array elements
27470                        path_string.push('[');
27471                        self.skip();
27472                        path_string.push('*');
27473                        self.expect(TokenType::RBracket)?;
27474                        path_string.push(']');
27475                    } else if self.check(TokenType::String) {
27476                        // Single-quoted string key access: ['bicycle']
27477                        // Convert to dot notation for simple keys, keep bracket notation for keys with spaces
27478                        let key = self.advance().text;
27479                        self.expect(TokenType::RBracket)?;
27480                        // Check if the key contains spaces or special characters that require bracket notation
27481                        let needs_brackets =
27482                            key.contains(' ') || key.contains('"') || key.contains('\'');
27483                        if needs_brackets {
27484                            // Keep bracket notation with double quotes: ["zip code"]
27485                            path_string.push_str("[\"");
27486                            for c in key.chars() {
27487                                if c == '"' {
27488                                    path_string.push_str("\\\"");
27489                                } else {
27490                                    path_string.push(c);
27491                                }
27492                            }
27493                            path_string.push_str("\"]");
27494                        } else {
27495                            // Convert to dot notation: store['bicycle'] -> store.bicycle
27496                            // But only add dot if path_string is not empty (handles c1:['price'] -> c1:price)
27497                            if !path_string.is_empty() {
27498                                path_string.push('.');
27499                            }
27500                            path_string.push_str(&key);
27501                        }
27502                    } else if self.check(TokenType::QuotedIdentifier) {
27503                        // Double-quoted string key access: ["zip code"]
27504                        // These are tokenized as QuotedIdentifier, not String
27505                        // Must be checked BEFORE is_identifier_token() since it includes QuotedIdentifier
27506                        let key = self.advance().text;
27507                        self.expect(TokenType::RBracket)?;
27508                        // Always use bracket notation with double quotes for quoted identifiers
27509                        path_string.push_str("[\"");
27510                        for c in key.chars() {
27511                            if c == '"' {
27512                                path_string.push_str("\\\"");
27513                            } else {
27514                                path_string.push(c);
27515                            }
27516                        }
27517                        path_string.push_str("\"]");
27518                    } else if self.is_identifier_token() {
27519                        // Check if this is a "dynamic bracket" — a column reference like s.x
27520                        // inside brackets. We detect this by checking if the identifier is
27521                        // followed by a dot (making it a qualified column reference).
27522                        let saved_bracket_pos = self.current;
27523                        let ident_text = self.advance().text.clone();
27524                        if self.check(TokenType::Dot) {
27525                            // Dynamic bracket: [s.x] where s.x is a column reference
27526                            // Backtrack to before the identifier so we can parse the full expression
27527                            self.current = saved_bracket_pos;
27528                            // Parse the full expression inside the brackets
27529                            let index_expr = self.parse_expression()?;
27530                            self.expect(TokenType::RBracket)?;
27531
27532                            // Build JSONExtract for the path accumulated so far
27533                            let path_expr =
27534                                Expression::Literal(Box::new(Literal::String(path_string)));
27535                            let json_extract = Expression::JSONExtract(Box::new(JSONExtract {
27536                                this: Box::new(this),
27537                                expression: Box::new(path_expr),
27538                                only_json_types: None,
27539                                expressions: Vec::new(),
27540                                variant_extract: Some(Box::new(Expression::Boolean(
27541                                    BooleanLiteral { value: true },
27542                                ))),
27543                                json_query: None,
27544                                option: None,
27545                                quote: None,
27546                                on_condition: None,
27547                                requires_json: None,
27548                            }));
27549
27550                            // Wrap in Subscript
27551                            let subscript = Expression::Subscript(Box::new(Subscript {
27552                                this: json_extract,
27553                                index: index_expr,
27554                            }));
27555
27556                            // Now continue parsing any remaining path after the dynamic bracket.
27557                            // This handles patterns like [s.x].r.d or [s.x]:r or [s.x].r.d[s.y]
27558                            // We parse dots into a new path string, and if we encounter another
27559                            // dynamic bracket, we recurse.
27560                            let mut suffix_path = String::new();
27561                            loop {
27562                                if self.match_token(TokenType::Dot) {
27563                                    // Dot access after dynamic bracket: [s.x].r.d
27564                                    if !suffix_path.is_empty() {
27565                                        suffix_path.push('.');
27566                                    }
27567                                    if self.is_identifier_token()
27568                                        || self.is_safe_keyword_as_identifier()
27569                                        || self.is_reserved_keyword_as_identifier()
27570                                    {
27571                                        let part = self.advance().text;
27572                                        suffix_path.push_str(&part);
27573                                    } else {
27574                                        return Err(self.parse_error(
27575                                            "Expected identifier after . in JSON path",
27576                                        ));
27577                                    }
27578                                } else if self.check(TokenType::LBracket) {
27579                                    // Another bracket after dot path: [s.x].r.d[s.y]
27580                                    // We need to check if this bracket contains a dynamic expression
27581                                    break;
27582                                } else {
27583                                    break;
27584                                }
27585                            }
27586
27587                            // Build the result depending on whether there are suffix dot paths
27588                            let result_base = if suffix_path.is_empty() {
27589                                subscript
27590                            } else {
27591                                // Create another JSONExtract for the suffix path
27592                                Expression::JSONExtract(Box::new(JSONExtract {
27593                                    this: Box::new(subscript),
27594                                    expression: Box::new(Expression::Literal(Box::new(
27595                                        Literal::String(suffix_path),
27596                                    ))),
27597                                    only_json_types: None,
27598                                    expressions: Vec::new(),
27599                                    variant_extract: Some(Box::new(Expression::Boolean(
27600                                        BooleanLiteral { value: true },
27601                                    ))),
27602                                    json_query: None,
27603                                    option: None,
27604                                    quote: None,
27605                                    on_condition: None,
27606                                    requires_json: None,
27607                                }))
27608                            };
27609
27610                            // Check for another bracket (e.g., [s.y] after .r.d)
27611                            if self.match_token(TokenType::LBracket) {
27612                                // Parse the index expression
27613                                let index_expr2 = self.parse_expression()?;
27614                                self.expect(TokenType::RBracket)?;
27615                                let subscript2 = Expression::Subscript(Box::new(Subscript {
27616                                    this: result_base,
27617                                    index: index_expr2,
27618                                }));
27619                                // Update `this` and `path_string` so we properly continue the outer loop
27620                                this = subscript2;
27621                                path_string = String::new();
27622                            } else {
27623                                this = result_base;
27624                                path_string = String::new();
27625                            }
27626
27627                            // Continue parsing more colon segments or break
27628                            // Need to break out of the inner loop to let the outer while loop
27629                            // check for more colon segments
27630                            break;
27631                        } else {
27632                            // Simple identifier index: [idx]
27633                            path_string.push('[');
27634                            path_string.push_str(&ident_text);
27635                            self.expect(TokenType::RBracket)?;
27636                            path_string.push(']');
27637                        }
27638                    } else {
27639                        // Empty brackets or unexpected token - just close the bracket
27640                        path_string.push('[');
27641                        self.expect(TokenType::RBracket)?;
27642                        path_string.push(']');
27643                    }
27644                } else if self.match_token(TokenType::Dot) {
27645                    // Handle dot access
27646                    path_string.push('.');
27647                    if self.is_identifier_token()
27648                        || self.is_safe_keyword_as_identifier()
27649                        || self.is_reserved_keyword_as_identifier()
27650                    {
27651                        let part = self.advance().text;
27652                        path_string.push_str(&part);
27653                    } else {
27654                        return Err(self.parse_error("Expected identifier after . in JSON path"));
27655                    }
27656                } else {
27657                    break;
27658                }
27659            }
27660        }
27661
27662        // If no path was parsed (e.g., backtracked on first colon), return the original expression
27663        if path_string.is_empty() {
27664            return Ok(this);
27665        }
27666
27667        // Create the JSONExtract expression with variant_extract marker
27668        let path_expr = Expression::Literal(Box::new(Literal::String(path_string)));
27669        let json_extract = Expression::JSONExtract(Box::new(JSONExtract {
27670            this: Box::new(this),
27671            expression: Box::new(path_expr),
27672            only_json_types: None,
27673            expressions: Vec::new(),
27674            variant_extract: Some(Box::new(Expression::Boolean(BooleanLiteral {
27675                value: true,
27676            }))),
27677            json_query: None,
27678            option: None,
27679            quote: None,
27680            on_condition: None,
27681            requires_json: None,
27682        }));
27683
27684        Ok(json_extract)
27685    }
27686
27687    /// Check if the current token is a reserved keyword that can be used as identifier in JSON path
27688    fn is_reserved_keyword_as_identifier(&self) -> bool {
27689        if self.is_at_end() {
27690            return false;
27691        }
27692        let token = self.peek();
27693        // Allow reserved keywords like FROM, SELECT, etc. as JSON path components
27694        matches!(
27695            token.token_type,
27696            TokenType::From
27697                | TokenType::Select
27698                | TokenType::Where
27699                | TokenType::And
27700                | TokenType::Or
27701                | TokenType::Not
27702                | TokenType::In
27703                | TokenType::As
27704                | TokenType::On
27705                | TokenType::Join
27706                | TokenType::Left
27707                | TokenType::Right
27708                | TokenType::Inner
27709                | TokenType::Outer
27710                | TokenType::Cross
27711                | TokenType::Full
27712                | TokenType::Group
27713                | TokenType::Order
27714                | TokenType::By
27715                | TokenType::Having
27716                | TokenType::Limit
27717                | TokenType::Offset
27718                | TokenType::Union
27719                | TokenType::Except
27720                | TokenType::Intersect
27721                | TokenType::All
27722                | TokenType::Distinct
27723                | TokenType::Case
27724                | TokenType::When
27725                | TokenType::Then
27726                | TokenType::Else
27727                | TokenType::End
27728                | TokenType::Null
27729                | TokenType::True
27730                | TokenType::False
27731                | TokenType::Between
27732                | TokenType::Like
27733                | TokenType::Is
27734                | TokenType::Exists
27735                | TokenType::Insert
27736                | TokenType::Update
27737                | TokenType::Delete
27738                | TokenType::Create
27739                | TokenType::Alter
27740                | TokenType::Drop
27741                | TokenType::Table
27742                | TokenType::View
27743                | TokenType::Index
27744                | TokenType::Set
27745                | TokenType::Values
27746                | TokenType::Into
27747                | TokenType::Default
27748                | TokenType::Key
27749                | TokenType::Unique
27750                | TokenType::Check
27751                | TokenType::Constraint
27752                | TokenType::References
27753        )
27754    }
27755
27756    /// Parse primary expressions
27757    fn parse_primary(&mut self) -> Result<Expression> {
27758        // Handle APPROXIMATE COUNT(DISTINCT expr) - Redshift syntax
27759        // Parses as ApproxDistinct expression
27760        if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("APPROXIMATE") {
27761            let saved_pos = self.current;
27762            self.skip(); // consume APPROXIMATE
27763                         // Parse the COUNT(DISTINCT ...) that follows
27764            let func = self.parse_primary()?;
27765            // Check if it's COUNT with DISTINCT
27766            if let Expression::Count(ref count_expr) = func {
27767                if count_expr.distinct {
27768                    let this_expr = count_expr.this.clone().unwrap_or_else(|| {
27769                        Expression::Star(crate::expressions::Star {
27770                            table: None,
27771                            except: None,
27772                            replace: None,
27773                            rename: None,
27774                            trailing_comments: Vec::new(),
27775                            span: None,
27776                        })
27777                    });
27778                    return Ok(Expression::ApproxDistinct(Box::new(
27779                        crate::expressions::AggFunc {
27780                            this: this_expr,
27781                            distinct: false,
27782                            filter: None,
27783                            order_by: Vec::new(),
27784                            name: Some("APPROX_DISTINCT".to_string()),
27785                            ignore_nulls: None,
27786                            having_max: None,
27787                            limit: None,
27788                            inferred_type: None,
27789                        },
27790                    )));
27791                }
27792            }
27793            // Not COUNT(DISTINCT ...) - backtrack
27794            self.current = saved_pos;
27795        }
27796
27797        if let Some(connect_by_root) = self.try_parse_connect_by_root_expression()? {
27798            return Ok(connect_by_root);
27799        }
27800
27801        // PostgreSQL VARIADIC prefix in function call arguments
27802        // e.g., SELECT MLEAST(VARIADIC ARRAY[10, -1, 5, 4.4])
27803        if matches!(
27804            self.config.dialect,
27805            Some(crate::dialects::DialectType::PostgreSQL)
27806                | Some(crate::dialects::DialectType::Redshift)
27807        ) {
27808            if self.check(TokenType::Var) && self.peek().text.eq_ignore_ascii_case("VARIADIC") {
27809                self.skip(); // consume VARIADIC
27810                let expr = self.parse_bitwise_or()?;
27811                return Ok(Expression::Variadic(Box::new(
27812                    crate::expressions::Variadic {
27813                        this: Box::new(expr),
27814                    },
27815                )));
27816            }
27817        }
27818
27819        // MySQL charset introducer: _utf8mb4 'string', _latin1 x'hex', etc.
27820        if matches!(
27821            self.config.dialect,
27822            Some(crate::dialects::DialectType::MySQL)
27823                | Some(crate::dialects::DialectType::SingleStore)
27824                | Some(crate::dialects::DialectType::Doris)
27825                | Some(crate::dialects::DialectType::StarRocks)
27826        ) {
27827            if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
27828                if self.peek().text.starts_with('_')
27829                    && Self::is_mysql_charset_introducer(&self.peek().text.to_ascii_uppercase())
27830                {
27831                    // Check if next token is a string literal or hex string
27832                    if self.current + 1 < self.tokens.len() {
27833                        let next_tt = self.tokens[self.current + 1].token_type;
27834                        if matches!(
27835                            next_tt,
27836                            TokenType::String | TokenType::HexString | TokenType::BitString
27837                        ) {
27838                            let charset_token = self.advance(); // consume charset name
27839                            let charset_name = charset_token.text.clone();
27840                            let literal = self.parse_primary()?; // parse the string/hex literal
27841                            return Ok(Expression::Introducer(Box::new(
27842                                crate::expressions::Introducer {
27843                                    this: Box::new(Expression::Column(Box::new(
27844                                        crate::expressions::Column {
27845                                            name: crate::expressions::Identifier {
27846                                                name: charset_name,
27847                                                quoted: false,
27848                                                trailing_comments: Vec::new(),
27849                                                span: None,
27850                                            },
27851                                            table: None,
27852                                            join_mark: false,
27853                                            trailing_comments: Vec::new(),
27854                                            span: None,
27855                                            inferred_type: None,
27856                                        },
27857                                    ))),
27858                                    expression: Box::new(literal),
27859                                },
27860                            )));
27861                        }
27862                    }
27863                }
27864            }
27865        }
27866
27867        // Array literal: [1, 2, 3] or comprehension: [expr FOR var IN iterator]
27868        if self.match_token(TokenType::LBracket) {
27869            // Parse empty array: []
27870            if self.match_token(TokenType::RBracket) {
27871                return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
27872                    expressions: Vec::new(),
27873                    bracket_notation: true,
27874                    use_list_keyword: false,
27875                })));
27876            }
27877
27878            // Parse first expression
27879            let first_expr = self.parse_expression()?;
27880
27881            // Check for comprehension syntax: [expr FOR var IN iterator [IF condition]]
27882            if self.match_token(TokenType::For) {
27883                // Parse loop variable - typically a simple identifier like 'x'
27884                let loop_var = self.parse_primary()?;
27885
27886                // Parse optional position (second variable after comma)
27887                let position = if self.match_token(TokenType::Comma) {
27888                    Some(self.parse_primary()?)
27889                } else {
27890                    None
27891                };
27892
27893                // Expect IN keyword
27894                if !self.match_token(TokenType::In) {
27895                    return Err(self.parse_error("Expected IN in comprehension"));
27896                }
27897
27898                // Parse iterator expression
27899                let iterator = self.parse_expression()?;
27900
27901                // Parse optional condition after IF
27902                let condition = if self.match_token(TokenType::If) {
27903                    Some(self.parse_expression()?)
27904                } else {
27905                    None
27906                };
27907
27908                // Expect closing bracket
27909                self.expect(TokenType::RBracket)?;
27910
27911                // Return Comprehension
27912                return Ok(Expression::Comprehension(Box::new(Comprehension {
27913                    this: Box::new(first_expr),
27914                    expression: Box::new(loop_var),
27915                    position: position.map(Box::new),
27916                    iterator: Some(Box::new(iterator)),
27917                    condition: condition.map(Box::new),
27918                })));
27919            }
27920
27921            // Regular array - continue parsing elements
27922            // ClickHouse allows AS aliases in array: [1 AS a, 2 AS b]
27923            let first_expr = if matches!(
27924                self.config.dialect,
27925                Some(crate::dialects::DialectType::ClickHouse)
27926            ) && self.check(TokenType::As)
27927                && !self.check_next(TokenType::RBracket)
27928            {
27929                self.skip(); // consume AS
27930                let alias = self.expect_identifier()?;
27931                Expression::Alias(Box::new(Alias::new(first_expr, Identifier::new(alias))))
27932            } else {
27933                first_expr
27934            };
27935            let mut expressions = vec![first_expr];
27936            while self.match_token(TokenType::Comma) {
27937                // Handle trailing comma
27938                if self.check(TokenType::RBracket) {
27939                    break;
27940                }
27941                let expr = self.parse_expression()?;
27942                // ClickHouse: handle AS alias on array elements
27943                let expr = if matches!(
27944                    self.config.dialect,
27945                    Some(crate::dialects::DialectType::ClickHouse)
27946                ) && self.check(TokenType::As)
27947                    && !self.check_next(TokenType::RBracket)
27948                {
27949                    self.skip(); // consume AS
27950                    let alias = self.expect_identifier()?;
27951                    Expression::Alias(Box::new(Alias::new(expr, Identifier::new(alias))))
27952                } else {
27953                    expr
27954                };
27955                expressions.push(expr);
27956            }
27957            self.expect(TokenType::RBracket)?;
27958            return self.maybe_parse_subscript(Expression::ArrayFunc(Box::new(ArrayConstructor {
27959                expressions,
27960                bracket_notation: true,
27961                use_list_keyword: false,
27962            })));
27963        }
27964
27965        // Map/Struct literal with curly braces: {'a': 1, 'b': 2}
27966        // Or Snowflake wildcard syntax: {*}, {tbl.*}, {* EXCLUDE (...)}, {* ILIKE '...'}
27967        if self.match_token(TokenType::LBrace) {
27968            // ClickHouse query parameter: {name: Type}
27969            // We consumed `{` above, so rewind and let the dedicated parser consume it.
27970            if matches!(
27971                self.config.dialect,
27972                Some(crate::dialects::DialectType::ClickHouse)
27973            ) {
27974                self.current -= 1;
27975                if let Some(param) = self.parse_clickhouse_braced_parameter()? {
27976                    return self.maybe_parse_subscript(param);
27977                }
27978                // Not a ClickHouse query parameter, restore position after `{` for map/wildcard parsing.
27979                self.current += 1;
27980            }
27981
27982            // Parse empty map: {}
27983            if self.match_token(TokenType::RBrace) {
27984                return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
27985                    keys: Vec::new(),
27986                    values: Vec::new(),
27987                    curly_brace_syntax: true,
27988                    with_map_keyword: false,
27989                })));
27990            }
27991
27992            // Check for ODBC escape syntax: {fn function_name(args)}
27993            // This must be checked before wildcards and map literals
27994            if self.check_identifier("fn") {
27995                self.skip(); // consume 'fn'
27996                             // Parse function call
27997                let func_name = self.expect_identifier_or_keyword_with_quoted()?;
27998                self.expect(TokenType::LParen)?;
27999
28000                // Parse function arguments
28001                let mut args = Vec::new();
28002                if !self.check(TokenType::RParen) {
28003                    loop {
28004                        args.push(self.parse_expression()?);
28005                        if !self.match_token(TokenType::Comma) {
28006                            break;
28007                        }
28008                    }
28009                }
28010                self.expect(TokenType::RParen)?;
28011                self.expect(TokenType::RBrace)?;
28012
28013                // Return as a regular function call (the ODBC escape is just syntax sugar)
28014                return Ok(Expression::Function(Box::new(Function::new(
28015                    func_name.name,
28016                    args,
28017                ))));
28018            }
28019
28020            // Check for ODBC datetime literals: {d'2024-01-01'}, {t'12:00:00'}, {ts'2024-01-01 12:00:00'}
28021            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
28022                let type_text = self.peek().text.to_lowercase();
28023                if (type_text == "d" || type_text == "t" || type_text == "ts")
28024                    && self.check_next(TokenType::String)
28025                {
28026                    self.skip(); // consume type indicator (d, t, or ts)
28027                    let value = self.expect_string()?;
28028                    self.expect(TokenType::RBrace)?;
28029
28030                    // Return appropriate expression based on type
28031                    return match type_text.as_str() {
28032                        "d" => Ok(Expression::Date(Box::new(
28033                            crate::expressions::UnaryFunc::new(Expression::Literal(Box::new(
28034                                crate::expressions::Literal::String(value),
28035                            ))),
28036                        ))),
28037                        "t" => Ok(Expression::Time(Box::new(
28038                            crate::expressions::UnaryFunc::new(Expression::Literal(Box::new(
28039                                crate::expressions::Literal::String(value),
28040                            ))),
28041                        ))),
28042                        "ts" => Ok(Expression::Timestamp(Box::new(
28043                            crate::expressions::TimestampFunc {
28044                                this: Some(Box::new(Expression::Literal(Box::new(
28045                                    crate::expressions::Literal::String(value),
28046                                )))),
28047                                zone: None,
28048                                with_tz: None,
28049                                safe: None,
28050                            },
28051                        ))),
28052                        _ => {
28053                            Err(self
28054                                .parse_error(format!("Unknown ODBC datetime type: {}", type_text)))
28055                        }
28056                    };
28057                }
28058            }
28059
28060            // Check for Snowflake wildcard syntax: {*}, {tbl.*}, {* EXCLUDE (...)}, {* ILIKE '...'}
28061            // Pattern: either {*...} or {identifier/var followed by .*}
28062            // Note: Identifiers may be tokenized as Var or Identifier
28063            let is_table_star = (self.check(TokenType::Identifier) || self.check(TokenType::Var))
28064                && self.check_next(TokenType::Dot)
28065                && self
28066                    .tokens
28067                    .get(self.current + 2)
28068                    .map(|t| t.token_type == TokenType::Star)
28069                    .unwrap_or(false);
28070            let is_wildcard = self.check(TokenType::Star) || is_table_star;
28071
28072            if is_wildcard {
28073                // Parse the wildcard expression
28074                let wildcard_expr = if self.match_token(TokenType::Star) {
28075                    // {*} or {* EXCLUDE ...} or {* ILIKE ...}
28076                    // Check for ILIKE first since it's different from standard star modifiers
28077                    if self.check_keyword_text("ILIKE") {
28078                        self.skip();
28079                        let pattern = self.parse_expression()?;
28080                        // Create an ILike expression with Star as left side
28081                        Expression::ILike(Box::new(LikeOp {
28082                            left: Expression::Star(Star {
28083                                table: None,
28084                                except: None,
28085                                replace: None,
28086                                rename: None,
28087                                trailing_comments: Vec::new(),
28088                                span: None,
28089                            }),
28090                            right: pattern,
28091                            escape: None,
28092                            quantifier: None,
28093                            inferred_type: None,
28094                        }))
28095                    } else {
28096                        // {*} or {* EXCLUDE ...}
28097                        let star = self.parse_star_modifiers(None)?;
28098                        Expression::Star(star)
28099                    }
28100                } else {
28101                    // {tbl.*} - table qualified wildcard
28102                    let table_name = self.expect_identifier_or_keyword_with_quoted()?;
28103                    self.expect(TokenType::Dot)?;
28104                    self.expect(TokenType::Star)?;
28105                    let star = self.parse_star_modifiers(Some(table_name))?;
28106                    Expression::Star(star)
28107                };
28108
28109                self.expect(TokenType::RBrace)?;
28110
28111                // Wrap in BracedWildcard for generation
28112                return Ok(Expression::BracedWildcard(Box::new(wildcard_expr)));
28113            }
28114
28115            // Parse key-value pairs: key: value, ...
28116            let mut keys = Vec::new();
28117            let mut values = Vec::new();
28118            loop {
28119                let key = self.parse_expression()?;
28120                self.expect(TokenType::Colon)?;
28121                let value = self.parse_expression()?;
28122                keys.push(key);
28123                values.push(value);
28124                if !self.match_token(TokenType::Comma) {
28125                    break;
28126                }
28127                // Handle trailing comma
28128                if self.check(TokenType::RBrace) {
28129                    break;
28130                }
28131            }
28132            self.expect(TokenType::RBrace)?;
28133            return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
28134                keys,
28135                values,
28136                curly_brace_syntax: true,
28137                with_map_keyword: false,
28138            })));
28139        }
28140
28141        // Parenthesized expression or subquery
28142        if self.match_token(TokenType::LParen) {
28143            // Capture comments from the ( token (e.g., "(/* comment */ 1)")
28144            let lparen_comments = self.previous_trailing_comments().to_vec();
28145
28146            // Empty parens () — could be empty tuple or zero-param lambda () -> body
28147            if self.check(TokenType::RParen) {
28148                self.skip(); // consume )
28149                             // Check for lambda: () -> body
28150                if self.match_token(TokenType::Arrow) || self.match_token(TokenType::FArrow) {
28151                    let body = self.parse_expression()?;
28152                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
28153                        parameters: Vec::new(),
28154                        body,
28155                        colon: false,
28156                        parameter_types: Vec::new(),
28157                    })));
28158                }
28159                // Otherwise empty tuple
28160                return self.maybe_parse_subscript(Expression::Tuple(Box::new(Tuple {
28161                    expressions: Vec::new(),
28162                })));
28163            }
28164
28165            // Check if this is a VALUES expression inside parens: (VALUES ...)
28166            if self.check(TokenType::Values) {
28167                let values = self.parse_values()?;
28168                self.expect(TokenType::RParen)?;
28169                return Ok(Expression::Subquery(Box::new(Subquery {
28170                    this: values,
28171                    alias: None,
28172                    column_aliases: Vec::new(),
28173                    order_by: None,
28174                    limit: None,
28175                    offset: None,
28176                    distribute_by: None,
28177                    sort_by: None,
28178                    cluster_by: None,
28179                    lateral: false,
28180                    modifiers_inside: false,
28181                    trailing_comments: self.previous_trailing_comments().to_vec(),
28182                    inferred_type: None,
28183                })));
28184            }
28185
28186            // Check if this is a subquery (SELECT, WITH, DuckDB FROM-first, or ClickHouse EXPLAIN)
28187            let is_explain_subquery = self.check(TokenType::Var)
28188                && self.peek().text.eq_ignore_ascii_case("EXPLAIN")
28189                && self.peek_nth(1).map_or(false, |t| {
28190                    // EXPLAIN followed by statement/style keywords is a subquery
28191                    matches!(
28192                        t.token_type,
28193                        TokenType::Select
28194                            | TokenType::Insert
28195                            | TokenType::Create
28196                            | TokenType::Alter
28197                            | TokenType::Drop
28198                            | TokenType::Set
28199                            | TokenType::System
28200                            | TokenType::Table
28201                    ) || matches!(
28202                        t.text.to_ascii_uppercase().as_str(),
28203                        "SYNTAX" | "AST" | "PLAN" | "PIPELINE" | "ESTIMATE" | "CURRENT" | "QUERY"
28204                    ) || (t.token_type == TokenType::Var
28205                        && self
28206                            .peek_nth(2)
28207                            .map_or(false, |t2| t2.token_type == TokenType::Eq))
28208                });
28209            // ClickHouse: (from, to, ...) -> body is a tuple-lambda with keyword params
28210            // Detect pattern: (keyword/ident, keyword/ident, ...) ->
28211            if matches!(
28212                self.config.dialect,
28213                Some(crate::dialects::DialectType::ClickHouse)
28214            ) {
28215                let mut look = self.current;
28216                let mut is_tuple_lambda = true;
28217                let mut param_count = 0;
28218                loop {
28219                    if look >= self.tokens.len() {
28220                        is_tuple_lambda = false;
28221                        break;
28222                    }
28223                    let tt = self.tokens[look].token_type;
28224                    if tt == TokenType::Identifier
28225                        || tt == TokenType::Var
28226                        || tt == TokenType::QuotedIdentifier
28227                        || tt.is_keyword()
28228                    {
28229                        param_count += 1;
28230                        look += 1;
28231                    } else {
28232                        is_tuple_lambda = false;
28233                        break;
28234                    }
28235                    if look >= self.tokens.len() {
28236                        is_tuple_lambda = false;
28237                        break;
28238                    }
28239                    if self.tokens[look].token_type == TokenType::Comma {
28240                        look += 1;
28241                    } else if self.tokens[look].token_type == TokenType::RParen {
28242                        look += 1;
28243                        break;
28244                    } else {
28245                        is_tuple_lambda = false;
28246                        break;
28247                    }
28248                }
28249                if is_tuple_lambda
28250                    && param_count >= 1
28251                    && look < self.tokens.len()
28252                    && self.tokens[look].token_type == TokenType::Arrow
28253                {
28254                    // Parse as lambda: consume params
28255                    let mut params = Vec::new();
28256                    loop {
28257                        let tok = self.advance();
28258                        params.push(Identifier::new(tok.text));
28259                        if self.match_token(TokenType::Comma) {
28260                            continue;
28261                        }
28262                        break;
28263                    }
28264                    self.expect(TokenType::RParen)?;
28265                    self.expect(TokenType::Arrow)?;
28266                    let body = self.parse_expression()?;
28267                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
28268                        parameters: params,
28269                        body,
28270                        colon: false,
28271                        parameter_types: Vec::new(),
28272                    })));
28273                }
28274            }
28275            if self.check(TokenType::Select)
28276                || self.check(TokenType::With)
28277                || self.check(TokenType::From)
28278                || is_explain_subquery
28279            {
28280                let query = self.parse_statement()?;
28281
28282                // Parse LIMIT/OFFSET that may appear after set operations INSIDE the parentheses
28283                // e.g., (SELECT 1 EXCEPT (SELECT 2) LIMIT 1)
28284                let limit = if self.match_token(TokenType::Limit) {
28285                    Some(Limit {
28286                        this: self.parse_expression()?,
28287                        percent: false,
28288                        comments: Vec::new(),
28289                    })
28290                } else {
28291                    None
28292                };
28293                let offset = if self.match_token(TokenType::Offset) {
28294                    Some(Offset {
28295                        this: self.parse_expression()?,
28296                        rows: None,
28297                    })
28298                } else {
28299                    None
28300                };
28301
28302                self.expect(TokenType::RParen)?;
28303
28304                // Wrap in Subquery to preserve parentheses in set operations
28305                let subquery = if limit.is_some() || offset.is_some() {
28306                    // If we have limit/offset INSIDE the parens, set modifiers_inside = true
28307                    Expression::Subquery(Box::new(Subquery {
28308                        this: query,
28309                        alias: None,
28310                        column_aliases: Vec::new(),
28311                        order_by: None,
28312                        limit,
28313                        offset,
28314                        distribute_by: None,
28315                        sort_by: None,
28316                        cluster_by: None,
28317                        lateral: false,
28318                        modifiers_inside: true,
28319                        trailing_comments: self.previous_trailing_comments().to_vec(),
28320                        inferred_type: None,
28321                    }))
28322                } else {
28323                    Expression::Subquery(Box::new(Subquery {
28324                        this: query,
28325                        alias: None,
28326                        column_aliases: Vec::new(),
28327                        order_by: None,
28328                        limit: None,
28329                        offset: None,
28330                        distribute_by: None,
28331                        sort_by: None,
28332                        cluster_by: None,
28333                        lateral: false,
28334                        modifiers_inside: false,
28335                        trailing_comments: self.previous_trailing_comments().to_vec(),
28336                        inferred_type: None,
28337                    }))
28338                };
28339
28340                // Check for set operations after the subquery (e.g., (SELECT 1) UNION (SELECT 2))
28341                let set_result = self.parse_set_operation(subquery)?;
28342
28343                // Only parse ORDER BY/LIMIT/OFFSET after set operations if there WAS a set operation
28344                // (for cases like ((SELECT 0) UNION (SELECT 1) ORDER BY 1 OFFSET 1))
28345                // If there's no set operation, we should NOT consume these - they belong to outer context
28346                let had_set_operation = matches!(
28347                    &set_result,
28348                    Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
28349                );
28350
28351                let result = if had_set_operation {
28352                    let order_by = if self.check(TokenType::Order) {
28353                        self.expect(TokenType::Order)?;
28354                        self.expect(TokenType::By)?;
28355                        Some(self.parse_order_by()?)
28356                    } else {
28357                        None
28358                    };
28359                    let limit_after = if self.match_token(TokenType::Limit) {
28360                        Some(Limit {
28361                            this: self.parse_expression()?,
28362                            percent: false,
28363                            comments: Vec::new(),
28364                        })
28365                    } else {
28366                        None
28367                    };
28368                    let offset_after = if self.match_token(TokenType::Offset) {
28369                        Some(Offset {
28370                            this: self.parse_expression()?,
28371                            rows: None,
28372                        })
28373                    } else {
28374                        None
28375                    };
28376
28377                    // If we have any modifiers, wrap in a Subquery with the modifiers OUTSIDE the paren
28378                    if order_by.is_some() || limit_after.is_some() || offset_after.is_some() {
28379                        Expression::Subquery(Box::new(Subquery {
28380                            this: set_result,
28381                            alias: None,
28382                            column_aliases: Vec::new(),
28383                            order_by,
28384                            limit: limit_after,
28385                            offset: offset_after,
28386                            lateral: false,
28387                            modifiers_inside: false,
28388                            trailing_comments: Vec::new(),
28389                            distribute_by: None,
28390                            sort_by: None,
28391                            cluster_by: None,
28392                            inferred_type: None,
28393                        }))
28394                    } else {
28395                        set_result
28396                    }
28397                } else {
28398                    set_result
28399                };
28400                // Allow postfix operators on subquery expressions (e.g., (SELECT 1, 2).1 for tuple element access)
28401                return self.maybe_parse_subscript(result);
28402            }
28403
28404            // Check if this starts with another paren that might be a subquery
28405            // e.g., ((SELECT 1))
28406            if self.check(TokenType::LParen) {
28407                let expr = self.parse_expression()?;
28408
28409                // Handle aliasing of expression inside outer parens (e.g., ((a, b) AS c))
28410                let first_expr = if self.match_token(TokenType::As) {
28411                    let alias = self.expect_identifier_or_alias_keyword_with_quoted()?;
28412                    Expression::Alias(Box::new(Alias::new(expr, alias)))
28413                } else {
28414                    expr
28415                };
28416
28417                // Check for tuple of tuples: ((1, 2), (3, 4))
28418                // Also handles ClickHouse: ((SELECT 1) AS x, (SELECT 2) AS y)
28419                if self.match_token(TokenType::Comma) {
28420                    let mut expressions = vec![first_expr];
28421                    loop {
28422                        if self.check(TokenType::RParen) {
28423                            break;
28424                        } // trailing comma
28425                        let elem = self.parse_expression()?;
28426                        // Handle AS alias after each element (ClickHouse tuple CTE pattern)
28427                        let elem = if self.match_token(TokenType::As) {
28428                            let alias = self.expect_identifier_or_keyword()?;
28429                            Expression::Alias(Box::new(Alias::new(elem, Identifier::new(alias))))
28430                        } else {
28431                            elem
28432                        };
28433                        expressions.push(elem);
28434                        if !self.match_token(TokenType::Comma) {
28435                            break;
28436                        }
28437                    }
28438                    self.expect(TokenType::RParen)?;
28439                    let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
28440                    return self.maybe_parse_subscript(tuple_expr);
28441                }
28442
28443                let result = first_expr;
28444
28445                self.expect(TokenType::RParen)?;
28446                let mut nested_paren_comments = lparen_comments.clone();
28447                nested_paren_comments.extend_from_slice(self.previous_trailing_comments());
28448                // Check for set operations after parenthesized expression
28449                if self.check(TokenType::Union)
28450                    || self.check(TokenType::Intersect)
28451                    || self.check(TokenType::Except)
28452                {
28453                    // This is a set operation - need to handle specially
28454                    if let Expression::Subquery(subq) = &result {
28455                        let set_result = self.parse_set_operation(subq.this.clone())?;
28456
28457                        // Parse ORDER BY/LIMIT/OFFSET after set operations
28458                        let order_by = if self.check(TokenType::Order) {
28459                            self.expect(TokenType::Order)?;
28460                            self.expect(TokenType::By)?;
28461                            Some(self.parse_order_by()?)
28462                        } else {
28463                            None
28464                        };
28465                        let limit = if self.match_token(TokenType::Limit) {
28466                            Some(Limit {
28467                                this: self.parse_expression()?,
28468                                percent: false,
28469                                comments: Vec::new(),
28470                            })
28471                        } else {
28472                            None
28473                        };
28474                        let offset = if self.match_token(TokenType::Offset) {
28475                            Some(Offset {
28476                                this: self.parse_expression()?,
28477                                rows: None,
28478                            })
28479                        } else {
28480                            None
28481                        };
28482
28483                        return Ok(Expression::Subquery(Box::new(Subquery {
28484                            this: set_result,
28485                            alias: None,
28486                            column_aliases: Vec::new(),
28487                            order_by,
28488                            limit,
28489                            offset,
28490                            lateral: false,
28491                            modifiers_inside: false,
28492                            trailing_comments: Vec::new(),
28493                            distribute_by: None,
28494                            sort_by: None,
28495                            cluster_by: None,
28496                            inferred_type: None,
28497                        })));
28498                    }
28499                }
28500                return self.maybe_parse_over(Expression::Paren(Box::new(Paren {
28501                    this: result,
28502                    trailing_comments: nested_paren_comments,
28503                })));
28504            }
28505
28506            let expr = self.parse_expression()?;
28507
28508            // Check for AS alias on the first element (e.g., (x AS y, ...))
28509            let first_expr = if self.match_token(TokenType::As) {
28510                let alias = self.expect_identifier_or_keyword_with_quoted()?;
28511                Expression::Alias(Box::new(Alias::new(expr, alias)))
28512            } else {
28513                expr
28514            };
28515
28516            // Check for tuple (multiple expressions separated by commas)
28517            if self.match_token(TokenType::Comma) {
28518                let mut expressions = vec![first_expr];
28519                // ClickHouse: trailing comma creates single-element tuple, e.g., (1,)
28520                if self.check(TokenType::RParen) {
28521                    self.skip(); // consume )
28522                    let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
28523                    return self.maybe_parse_subscript(tuple_expr);
28524                }
28525                // Parse remaining tuple elements, each can have AS alias
28526                loop {
28527                    let elem = self.parse_expression()?;
28528                    let elem_with_alias = if self.match_token(TokenType::As) {
28529                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
28530                        Expression::Alias(Box::new(Alias::new(elem, alias)))
28531                    } else {
28532                        elem
28533                    };
28534                    expressions.push(elem_with_alias);
28535                    if !self.match_token(TokenType::Comma) {
28536                        break;
28537                    }
28538                    // ClickHouse: trailing comma in multi-element tuple, e.g., (1, 2,)
28539                    if self.check(TokenType::RParen) {
28540                        break;
28541                    }
28542                }
28543
28544                self.expect(TokenType::RParen)?;
28545
28546                // Check for lambda expression: (a, b) -> body
28547                if self.match_token(TokenType::Arrow) {
28548                    let parameters = expressions
28549                        .into_iter()
28550                        .filter_map(|e| {
28551                            if let Expression::Column(c) = e {
28552                                Some(c.name)
28553                            } else if let Expression::Identifier(id) = e {
28554                                Some(id)
28555                            } else {
28556                                None
28557                            }
28558                        })
28559                        .collect();
28560                    let body = self.parse_expression()?;
28561                    return Ok(Expression::Lambda(Box::new(LambdaExpr {
28562                        parameters,
28563                        body,
28564                        colon: false,
28565                        parameter_types: Vec::new(),
28566                    })));
28567                }
28568
28569                // Check for optional alias on the whole tuple
28570                // But NOT when AS is followed by a type constructor like Tuple(a Int8, ...)
28571                // or STRUCT<a TINYINT, ...> which would be part of a CAST expression: CAST((1, 2) AS Tuple(a Int8, b Int16))
28572                // Also NOT when AS is followed by a type name then ) like: CAST((1, 2) AS String)
28573                let tuple_expr = Expression::Tuple(Box::new(Tuple { expressions }));
28574                let result = if self.check(TokenType::As) {
28575                    // Look ahead: AS + type_keyword + ( or < → likely a type, not an alias
28576                    let after_as = self.current + 1;
28577                    let after_ident = self.current + 2;
28578                    let is_type_constructor = after_ident < self.tokens.len()
28579                        && (self.tokens[after_as].token_type == TokenType::Identifier
28580                            || self.tokens[after_as].token_type == TokenType::Var
28581                            || self.tokens[after_as].token_type == TokenType::Nullable
28582                            || self.tokens[after_as].token_type == TokenType::Struct
28583                            || self.tokens[after_as].token_type == TokenType::Array)
28584                        && (self.tokens[after_ident].token_type == TokenType::LParen
28585                            || self.tokens[after_ident].token_type == TokenType::Lt);
28586                    // Check if AS is followed by identifier/keyword then ), indicating CAST(tuple AS Type)
28587                    let is_cast_type = after_ident < self.tokens.len()
28588                        && (self.tokens[after_as].token_type == TokenType::Identifier
28589                            || self.tokens[after_as].token_type == TokenType::Var
28590                            || self.tokens[after_as].token_type.is_keyword())
28591                        && self.tokens[after_ident].token_type == TokenType::RParen;
28592                    if is_type_constructor || is_cast_type {
28593                        tuple_expr
28594                    } else {
28595                        self.skip(); // consume AS
28596                        let alias = self.expect_identifier()?;
28597                        Expression::Alias(Box::new(Alias::new(tuple_expr, Identifier::new(alias))))
28598                    }
28599                } else {
28600                    tuple_expr
28601                };
28602
28603                // Allow postfix operators on tuple expressions (e.g., ('a', 'b').1 for tuple element access)
28604                return self.maybe_parse_subscript(result);
28605            }
28606
28607            // ClickHouse: (x -> body) — lambda inside parentheses
28608            if matches!(
28609                self.config.dialect,
28610                Some(crate::dialects::DialectType::ClickHouse)
28611            ) && self.match_token(TokenType::Arrow)
28612            {
28613                let parameters = if let Expression::Column(c) = first_expr {
28614                    vec![c.name]
28615                } else if let Expression::Identifier(id) = first_expr {
28616                    vec![id]
28617                } else {
28618                    return Err(self.parse_error("Expected identifier as lambda parameter"));
28619                };
28620                let body = self.parse_expression()?;
28621                self.expect(TokenType::RParen)?;
28622                return Ok(Expression::Paren(Box::new(Paren {
28623                    this: Expression::Lambda(Box::new(LambdaExpr {
28624                        parameters,
28625                        body,
28626                        colon: false,
28627                        parameter_types: Vec::new(),
28628                    })),
28629                    trailing_comments: Vec::new(),
28630                })));
28631            }
28632
28633            self.expect(TokenType::RParen)?;
28634            // Combine comments from ( and ) tokens
28635            let mut paren_comments = lparen_comments.clone();
28636            paren_comments.extend_from_slice(self.previous_trailing_comments());
28637
28638            // Check for lambda expression: (x) -> body or single identifier case
28639            if self.match_token(TokenType::Arrow) {
28640                // first_expr should be a single identifier for the parameter
28641                let parameters = if let Expression::Column(c) = first_expr {
28642                    vec![c.name]
28643                } else if let Expression::Identifier(id) = first_expr {
28644                    vec![id]
28645                } else {
28646                    return Err(self.parse_error("Expected identifier as lambda parameter"));
28647                };
28648                let body = self.parse_expression()?;
28649                return Ok(Expression::Lambda(Box::new(LambdaExpr {
28650                    parameters,
28651                    body,
28652                    colon: false,
28653                    parameter_types: Vec::new(),
28654                })));
28655            }
28656
28657            return self.maybe_parse_over(Expression::Paren(Box::new(Paren {
28658                this: first_expr,
28659                trailing_comments: paren_comments,
28660            })));
28661        }
28662
28663        // NULL
28664        if self.match_token(TokenType::Null) {
28665            return Ok(Expression::Null(Null));
28666        }
28667
28668        // TRUE
28669        if self.match_token(TokenType::True) {
28670            return Ok(Expression::Boolean(BooleanLiteral { value: true }));
28671        }
28672
28673        // FALSE
28674        if self.match_token(TokenType::False) {
28675            return Ok(Expression::Boolean(BooleanLiteral { value: false }));
28676        }
28677
28678        // LAMBDA expression (DuckDB syntax: LAMBDA x : expr)
28679        if self.check(TokenType::Lambda) {
28680            if let Some(lambda) = self.parse_lambda()? {
28681                return Ok(lambda);
28682            }
28683        }
28684
28685        // CASE expression - but not if followed by DOT (then it's an identifier like case.column)
28686        if self.check(TokenType::Case) && !self.check_next(TokenType::Dot) {
28687            let case_expr = self.parse_case()?;
28688            return self.maybe_parse_over(case_expr);
28689        }
28690
28691        // CAST expression
28692        if self.check(TokenType::Cast) {
28693            let cast_expr = self.parse_cast()?;
28694            return self.maybe_parse_subscript(cast_expr);
28695        }
28696
28697        // TRY_CAST expression
28698        if self.check(TokenType::TryCast) {
28699            let cast_expr = self.parse_try_cast()?;
28700            return self.maybe_parse_subscript(cast_expr);
28701        }
28702
28703        // SAFE_CAST expression (BigQuery)
28704        if self.check(TokenType::SafeCast) {
28705            let cast_expr = self.parse_safe_cast()?;
28706            return self.maybe_parse_subscript(cast_expr);
28707        }
28708
28709        // EXISTS - either subquery predicate EXISTS(SELECT ...) or Hive array function EXISTS(array, lambda)
28710        // ClickHouse: EXISTS without ( is a column name/identifier
28711        if self.check(TokenType::Exists)
28712            && matches!(
28713                self.config.dialect,
28714                Some(crate::dialects::DialectType::ClickHouse)
28715            )
28716            && !self.check_next(TokenType::LParen)
28717        {
28718            let tok = self.advance();
28719            return Ok(Expression::Identifier(Identifier::new(tok.text)));
28720        }
28721        if self.match_token(TokenType::Exists) {
28722            self.expect(TokenType::LParen)?;
28723
28724            // Check if this is a subquery EXISTS (SELECT, WITH, or FROM for DuckDB)
28725            // ClickHouse: also handle EXISTS((SELECT ...)) with double parens
28726            if self.check(TokenType::Select)
28727                || self.check(TokenType::With)
28728                || self.check(TokenType::From)
28729                || (self.check(TokenType::LParen)
28730                    && self
28731                        .peek_nth(1)
28732                        .map(|t| {
28733                            matches!(
28734                                t.token_type,
28735                                TokenType::Select | TokenType::With | TokenType::From
28736                            )
28737                        })
28738                        .unwrap_or(false))
28739            {
28740                let query = self.parse_statement()?;
28741                self.expect(TokenType::RParen)?;
28742                return Ok(Expression::Exists(Box::new(Exists {
28743                    this: query,
28744                    not: false,
28745                })));
28746            }
28747
28748            // Otherwise it's Hive's array EXISTS function: EXISTS(array, lambda_predicate)
28749            // This function checks if any element in the array matches the predicate
28750            let array_expr = self.parse_expression()?;
28751            self.expect(TokenType::Comma)?;
28752            let predicate = self.parse_expression()?;
28753            self.expect(TokenType::RParen)?;
28754            return Ok(Expression::Function(Box::new(Function {
28755                name: "EXISTS".to_string(),
28756                args: vec![array_expr, predicate],
28757                distinct: false,
28758                trailing_comments: Vec::new(),
28759                use_bracket_syntax: false,
28760                no_parens: false,
28761                quoted: false,
28762                span: None,
28763                inferred_type: None,
28764            })));
28765        }
28766
28767        // INTERVAL expression or identifier
28768        if self.check(TokenType::Interval) {
28769            if let Some(interval_expr) = self.try_parse_interval()? {
28770                return Ok(interval_expr);
28771            }
28772            // INTERVAL is used as an identifier
28773            let token = self.advance();
28774            return Ok(Expression::Identifier(Identifier::new(token.text)));
28775        }
28776
28777        // DATE literal: DATE '2024-01-15' or DATE function: DATE(expr)
28778        if self.check(TokenType::Date) {
28779            let token = self.advance();
28780            let original_text = token.text.clone();
28781            if self.check(TokenType::String) {
28782                let str_token = self.advance();
28783                if self.config.dialect.is_none() {
28784                    // Generic (no dialect): DATE 'literal' -> CAST('literal' AS DATE)
28785                    return Ok(Expression::Cast(Box::new(Cast {
28786                        this: Expression::Literal(Box::new(Literal::String(str_token.text))),
28787                        to: DataType::Date,
28788                        trailing_comments: Vec::new(),
28789                        double_colon_syntax: false,
28790                        format: None,
28791                        default: None,
28792                        inferred_type: None,
28793                    })));
28794                }
28795                return Ok(Expression::Literal(Box::new(Literal::Date(str_token.text))));
28796            }
28797            // Check for DATE() function call
28798            if self.match_token(TokenType::LParen) {
28799                let func_expr = self.parse_typed_function(&original_text, "DATE", false)?;
28800                return self.maybe_parse_over(func_expr);
28801            }
28802            // Fallback to DATE as column reference - preserve original case
28803            return Ok(Expression::boxed_column(Column {
28804                name: Identifier::new(original_text),
28805                table: None,
28806                join_mark: false,
28807                trailing_comments: Vec::new(),
28808                span: None,
28809                inferred_type: None,
28810            }));
28811        }
28812
28813        // TIME literal: TIME '10:30:00' or TIME function: TIME(expr)
28814        if self.check(TokenType::Time) {
28815            let token = self.advance();
28816            let original_text = token.text.clone();
28817            if self.check(TokenType::String) {
28818                let str_token = self.advance();
28819                return Ok(Expression::Literal(Box::new(Literal::Time(str_token.text))));
28820            }
28821            // Check for TIME() function call
28822            if self.match_token(TokenType::LParen) {
28823                let func_expr = self.parse_typed_function(&original_text, "TIME", false)?;
28824                return self.maybe_parse_over(func_expr);
28825            }
28826            // Fallback to TIME as column reference - preserve original case
28827            return self.maybe_parse_subscript(Expression::boxed_column(Column {
28828                name: Identifier::new(original_text),
28829                table: None,
28830                join_mark: false,
28831                trailing_comments: Vec::new(),
28832                span: None,
28833                inferred_type: None,
28834            }));
28835        }
28836
28837        // TIMESTAMP literal: TIMESTAMP '2024-01-15 10:30:00' or TIMESTAMP function: TIMESTAMP(expr)
28838        // Also handles TIMESTAMP(n) WITH TIME ZONE as a data type expression
28839        if self.check(TokenType::Timestamp) {
28840            let token = self.advance();
28841            let original_text = token.text.clone();
28842            if self.check(TokenType::String) {
28843                let str_token = self.advance();
28844                if self.config.dialect.is_none() {
28845                    // Generic (no dialect): TIMESTAMP 'literal' -> CAST('literal' AS TIMESTAMP)
28846                    return Ok(Expression::Cast(Box::new(Cast {
28847                        this: Expression::Literal(Box::new(Literal::String(str_token.text))),
28848                        to: DataType::Timestamp {
28849                            precision: None,
28850                            timezone: false,
28851                        },
28852                        trailing_comments: Vec::new(),
28853                        double_colon_syntax: false,
28854                        format: None,
28855                        default: None,
28856                        inferred_type: None,
28857                    })));
28858                }
28859                // Dialect-specific: keep as Literal::Timestamp for dialect transforms
28860                return Ok(Expression::Literal(Box::new(Literal::Timestamp(
28861                    str_token.text,
28862                ))));
28863            }
28864            // Check for TIMESTAMP(n) WITH/WITHOUT TIME ZONE or TIMESTAMP(n) 'literal' as data type
28865            // This is a data type, not a function call
28866            if self.check(TokenType::LParen) {
28867                // Look ahead to see if this is TIMESTAMP(number) WITH/WITHOUT/String (data type)
28868                // vs TIMESTAMP(expr) (function call)
28869                let is_data_type = self.check_next(TokenType::Number) && {
28870                    // Check if after (number) there's WITH, WITHOUT, or String literal
28871                    let mut lookahead = self.current + 2;
28872                    // Skip the number
28873                    while lookahead < self.tokens.len()
28874                        && self.tokens[lookahead].token_type == TokenType::RParen
28875                    {
28876                        lookahead += 1;
28877                        break;
28878                    }
28879                    // Check for WITH, WITHOUT, or String after the closing paren
28880                    lookahead < self.tokens.len()
28881                        && (self.tokens[lookahead].token_type == TokenType::With
28882                            || self.tokens[lookahead].text.eq_ignore_ascii_case("WITHOUT")
28883                            || self.tokens[lookahead].token_type == TokenType::String)
28884                };
28885
28886                if is_data_type {
28887                    // Parse as data type: TIMESTAMP(precision) [WITH/WITHOUT TIME ZONE] ['literal']
28888                    self.skip(); // consume (
28889                    let precision = Some(self.expect_number()? as u32);
28890                    self.expect(TokenType::RParen)?;
28891
28892                    let data_type = if self.match_token(TokenType::With) {
28893                        if self.match_token(TokenType::Local) {
28894                            // WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
28895                            self.match_keyword("TIME");
28896                            self.match_keyword("ZONE");
28897                            DataType::Custom {
28898                                name: format!("TIMESTAMPLTZ({})", precision.unwrap()),
28899                            }
28900                        } else {
28901                            self.match_keyword("TIME");
28902                            self.match_keyword("ZONE");
28903                            DataType::Timestamp {
28904                                precision,
28905                                timezone: true,
28906                            }
28907                        }
28908                    } else if self.match_keyword("WITHOUT") {
28909                        self.match_keyword("TIME");
28910                        self.match_keyword("ZONE");
28911                        DataType::Timestamp {
28912                            precision,
28913                            timezone: false,
28914                        }
28915                    } else {
28916                        DataType::Timestamp {
28917                            precision,
28918                            timezone: false,
28919                        }
28920                    };
28921
28922                    // Check for following string literal -> wrap in CAST
28923                    if self.check(TokenType::String) {
28924                        let str_token = self.advance();
28925                        return Ok(Expression::Cast(Box::new(Cast {
28926                            this: Expression::Literal(Box::new(Literal::String(str_token.text))),
28927                            to: data_type,
28928                            trailing_comments: Vec::new(),
28929                            double_colon_syntax: false,
28930                            format: None,
28931                            default: None,
28932                            inferred_type: None,
28933                        })));
28934                    }
28935
28936                    return Ok(Expression::DataType(data_type));
28937                }
28938
28939                // Otherwise parse as function call
28940                self.skip(); // consume (
28941                let func_expr = self.parse_typed_function(&original_text, "TIMESTAMP", false)?;
28942                return self.maybe_parse_over(func_expr);
28943            }
28944            // Check for TIMESTAMP WITH/WITHOUT TIME ZONE (no precision) as data type
28945            // Use lookahead to verify WITH is followed by TIME (not WITH FILL, WITH TOTALS, etc.)
28946            if (self.check(TokenType::With)
28947                && self.peek_nth(1).map_or(false, |t| {
28948                    t.text.eq_ignore_ascii_case("TIME") || t.text.eq_ignore_ascii_case("LOCAL")
28949                }))
28950                || self.check_keyword_text("WITHOUT")
28951            {
28952                let data_type = if self.match_token(TokenType::With) {
28953                    if self.match_token(TokenType::Local) {
28954                        // WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
28955                        self.match_keyword("TIME");
28956                        self.match_keyword("ZONE");
28957                        DataType::Custom {
28958                            name: "TIMESTAMPLTZ".to_string(),
28959                        }
28960                    } else {
28961                        self.match_keyword("TIME");
28962                        self.match_keyword("ZONE");
28963                        DataType::Timestamp {
28964                            precision: None,
28965                            timezone: true,
28966                        }
28967                    }
28968                } else if self.match_keyword("WITHOUT") {
28969                    self.match_keyword("TIME");
28970                    self.match_keyword("ZONE");
28971                    DataType::Timestamp {
28972                        precision: None,
28973                        timezone: false,
28974                    }
28975                } else {
28976                    DataType::Timestamp {
28977                        precision: None,
28978                        timezone: false,
28979                    }
28980                };
28981
28982                // Check for following string literal -> wrap in CAST
28983                if self.check(TokenType::String) {
28984                    let str_token = self.advance();
28985                    return Ok(Expression::Cast(Box::new(Cast {
28986                        this: Expression::Literal(Box::new(Literal::String(str_token.text))),
28987                        to: data_type,
28988                        trailing_comments: Vec::new(),
28989                        double_colon_syntax: false,
28990                        format: None,
28991                        default: None,
28992                        inferred_type: None,
28993                    })));
28994                }
28995
28996                return Ok(Expression::DataType(data_type));
28997            }
28998            // Fallback to TIMESTAMP as column reference - preserve original case
28999            return Ok(Expression::boxed_column(Column {
29000                name: Identifier::new(original_text),
29001                table: None,
29002                join_mark: false,
29003                trailing_comments: Vec::new(),
29004                span: None,
29005                inferred_type: None,
29006            }));
29007        }
29008
29009        // DATETIME literal: DATETIME '2024-01-15 10:30:00' or DATETIME function: DATETIME(expr)
29010        if self.check(TokenType::DateTime) {
29011            let token = self.advance();
29012            let original_text = token.text.clone();
29013            if self.check(TokenType::String) {
29014                let str_token = self.advance();
29015                return Ok(Expression::Literal(Box::new(Literal::Datetime(
29016                    str_token.text,
29017                ))));
29018            }
29019            // Check for DATETIME() function call
29020            if self.match_token(TokenType::LParen) {
29021                let func_expr = self.parse_typed_function(&original_text, "DATETIME", false)?;
29022                return self.maybe_parse_over(func_expr);
29023            }
29024            // Fallback to DATETIME as column reference - preserve original case
29025            return Ok(Expression::boxed_column(Column {
29026                name: Identifier::new(original_text),
29027                table: None,
29028                join_mark: false,
29029                trailing_comments: Vec::new(),
29030                span: None,
29031                inferred_type: None,
29032            }));
29033        }
29034
29035        // ROW() function (window function for row number)
29036        if self.check(TokenType::Row) && self.check_next(TokenType::LParen) {
29037            self.skip(); // consume ROW
29038            self.expect(TokenType::LParen)?;
29039            // ROW() typically takes no arguments
29040            let args = if !self.check(TokenType::RParen) {
29041                self.parse_expression_list()?
29042            } else {
29043                Vec::new()
29044            };
29045            self.expect(TokenType::RParen)?;
29046            let func_expr = Expression::Function(Box::new(Function {
29047                name: "ROW".to_string(),
29048                args,
29049                distinct: false,
29050                trailing_comments: Vec::new(),
29051                use_bracket_syntax: false,
29052                no_parens: false,
29053                quoted: false,
29054                span: None,
29055                inferred_type: None,
29056            }));
29057            return self.maybe_parse_over(func_expr);
29058        }
29059
29060        // Number - support postfix operators like ::type
29061        if self.check(TokenType::Number) {
29062            let token = self.advance();
29063            if matches!(
29064                self.config.dialect,
29065                Some(crate::dialects::DialectType::MySQL)
29066            ) {
29067                let text = token.text.as_str();
29068                if text.len() > 2
29069                    && (text.starts_with("0x") || text.starts_with("0X"))
29070                    && !text[2..].chars().all(|c| c.is_ascii_hexdigit())
29071                {
29072                    let ident = Expression::Identifier(Identifier {
29073                        name: token.text,
29074                        quoted: true,
29075                        trailing_comments: Vec::new(),
29076                        span: None,
29077                    });
29078                    return self.maybe_parse_subscript(ident);
29079                }
29080            }
29081            if matches!(
29082                self.config.dialect,
29083                Some(crate::dialects::DialectType::Teradata)
29084            ) && token.text == "0"
29085            {
29086                if let Some(next) = self.tokens.get(self.current) {
29087                    let is_adjacent = token.span.end == next.span.start;
29088                    let next_text = next.text.as_str();
29089                    let is_hex_prefix = next_text.starts_with('x') || next_text.starts_with('X');
29090                    if is_adjacent
29091                        && matches!(next.token_type, TokenType::Identifier | TokenType::Var)
29092                        && is_hex_prefix
29093                        && next_text.len() > 1
29094                        && next_text[1..].chars().all(|c| c.is_ascii_hexdigit())
29095                    {
29096                        // Consume the hex suffix token and emit a HexString literal
29097                        let hex_token = self.advance();
29098                        let hex = hex_token.text[1..].to_string();
29099                        let literal = Expression::Literal(Box::new(Literal::HexString(hex)));
29100                        return self.maybe_parse_subscript(literal);
29101                    }
29102                }
29103            }
29104            if matches!(
29105                self.config.dialect,
29106                Some(crate::dialects::DialectType::ClickHouse)
29107            ) {
29108                if let Some(next) = self.tokens.get(self.current) {
29109                    let is_adjacent = token.span.end == next.span.start;
29110                    if is_adjacent
29111                        && matches!(next.token_type, TokenType::Identifier | TokenType::Var)
29112                        && next.text.starts_with('_')
29113                    {
29114                        let suffix = next.text.clone();
29115                        self.skip(); // consume suffix token
29116                        let combined = format!("{}{}", token.text, suffix);
29117                        let literal = Expression::Literal(Box::new(Literal::Number(combined)));
29118                        return self.maybe_parse_subscript(literal);
29119                    }
29120                }
29121            }
29122            // Check for numeric literal suffix encoded as "number::TYPE" by tokenizer
29123            let literal = if let Some(sep_pos) = token.text.find("::") {
29124                let num_part = &token.text[..sep_pos];
29125                let type_name = &token.text[sep_pos + 2..];
29126                let num_expr = Expression::Literal(Box::new(Literal::Number(num_part.to_string())));
29127                let data_type = match type_name {
29128                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
29129                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
29130                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
29131                    "DOUBLE" => crate::expressions::DataType::Double {
29132                        precision: None,
29133                        scale: None,
29134                    },
29135                    "FLOAT" => crate::expressions::DataType::Float {
29136                        precision: None,
29137                        scale: None,
29138                        real_spelling: false,
29139                    },
29140                    "DECIMAL" => crate::expressions::DataType::Decimal {
29141                        precision: None,
29142                        scale: None,
29143                    },
29144                    _ => crate::expressions::DataType::Custom {
29145                        name: type_name.to_string(),
29146                    },
29147                };
29148                Expression::Cast(Box::new(crate::expressions::Cast {
29149                    this: num_expr,
29150                    to: data_type,
29151                    trailing_comments: Vec::new(),
29152                    double_colon_syntax: false,
29153                    format: None,
29154                    default: None,
29155                    inferred_type: None,
29156                }))
29157            } else {
29158                Expression::Literal(Box::new(Literal::Number(token.text)))
29159            };
29160            return self.maybe_parse_subscript(literal);
29161        }
29162
29163        // String - support postfix operators like ::type, ->, ->>
29164        // Also handle adjacent string literals (SQL standard) which concatenate: 'x' 'y' 'z' -> CONCAT('x', 'y', 'z')
29165        if self.check(TokenType::String) {
29166            let token = self.advance();
29167            let first_literal = Expression::Literal(Box::new(Literal::String(token.text)));
29168
29169            // Check for adjacent string literals (PostgreSQL and SQL standard feature)
29170            // 'x' 'y' 'z' should be treated as string concatenation
29171            if self.check(TokenType::String) {
29172                let mut expressions = vec![first_literal];
29173                while self.check(TokenType::String) {
29174                    let next_token = self.advance();
29175                    expressions.push(Expression::Literal(Box::new(Literal::String(
29176                        next_token.text,
29177                    ))));
29178                }
29179                // Create CONCAT function call with all adjacent strings
29180                let concat_func =
29181                    Expression::Function(Box::new(Function::new("CONCAT", expressions)));
29182                return self.maybe_parse_subscript(concat_func);
29183            }
29184
29185            return self.maybe_parse_subscript(first_literal);
29186        }
29187
29188        // Dollar-quoted string: $$...$$ or $tag$...$tag$ -- preserve as DollarString
29189        // so the generator can handle dialect-specific conversion
29190        if self.check(TokenType::DollarString) {
29191            let token = self.advance();
29192            let literal = Expression::Literal(Box::new(Literal::DollarString(token.text)));
29193            return self.maybe_parse_subscript(literal);
29194        }
29195
29196        // Triple-quoted string with double quotes: """..."""
29197        if self.check(TokenType::TripleDoubleQuotedString) {
29198            let token = self.advance();
29199            let literal =
29200                Expression::Literal(Box::new(Literal::TripleQuotedString(token.text, '"')));
29201            return self.maybe_parse_subscript(literal);
29202        }
29203
29204        // Triple-quoted string with single quotes: '''...'''
29205        if self.check(TokenType::TripleSingleQuotedString) {
29206            let token = self.advance();
29207            let literal =
29208                Expression::Literal(Box::new(Literal::TripleQuotedString(token.text, '\'')));
29209            return self.maybe_parse_subscript(literal);
29210        }
29211
29212        // National String (N'...')
29213        if self.check(TokenType::NationalString) {
29214            let token = self.advance();
29215            let literal = Expression::Literal(Box::new(Literal::NationalString(token.text)));
29216            return self.maybe_parse_subscript(literal);
29217        }
29218
29219        // Hex String (X'...')
29220        if self.check(TokenType::HexString) {
29221            let token = self.advance();
29222            let literal = Expression::Literal(Box::new(Literal::HexString(token.text)));
29223            return self.maybe_parse_subscript(literal);
29224        }
29225
29226        // Hex Number (0xA from BigQuery/SQLite) - integer in hex notation
29227        if self.check(TokenType::HexNumber) {
29228            let token = self.advance();
29229            if matches!(
29230                self.config.dialect,
29231                Some(crate::dialects::DialectType::MySQL)
29232            ) {
29233                let text = token.text.as_str();
29234                if text.len() > 2
29235                    && (text.starts_with("0x") || text.starts_with("0X"))
29236                    && !text[2..].chars().all(|c| c.is_ascii_hexdigit())
29237                {
29238                    let ident = Expression::Identifier(Identifier {
29239                        name: token.text,
29240                        quoted: true,
29241                        trailing_comments: Vec::new(),
29242                        span: None,
29243                    });
29244                    return self.maybe_parse_subscript(ident);
29245                }
29246            }
29247            let literal = Expression::Literal(Box::new(Literal::HexNumber(token.text)));
29248            return self.maybe_parse_subscript(literal);
29249        }
29250
29251        // Bit String (B'...')
29252        if self.check(TokenType::BitString) {
29253            let token = self.advance();
29254            let literal = Expression::Literal(Box::new(Literal::BitString(token.text)));
29255            return self.maybe_parse_subscript(literal);
29256        }
29257
29258        // Byte String (b"..." - BigQuery style)
29259        if self.check(TokenType::ByteString) {
29260            let token = self.advance();
29261            let literal = Expression::Literal(Box::new(Literal::ByteString(token.text)));
29262            return self.maybe_parse_subscript(literal);
29263        }
29264
29265        // Raw String (r"..." - BigQuery style, backslashes are literal)
29266        if self.check(TokenType::RawString) {
29267            let token = self.advance();
29268            // Raw strings preserve backslashes as literal characters.
29269            // The generator will handle escaping when converting to a regular string.
29270            let literal = Expression::Literal(Box::new(Literal::RawString(token.text)));
29271            return self.maybe_parse_subscript(literal);
29272        }
29273
29274        // Escape String (E'...' - PostgreSQL)
29275        if self.check(TokenType::EscapeString) {
29276            let token = self.advance();
29277            // EscapeString is stored as "E'content'" - extract just the content
29278            let literal = Expression::Literal(Box::new(Literal::EscapeString(token.text)));
29279            return self.maybe_parse_subscript(literal);
29280        }
29281
29282        // Star - check for DuckDB *COLUMNS(...) syntax first
29283        if self.check(TokenType::Star) {
29284            // DuckDB *COLUMNS(...) syntax: *COLUMNS(*), *COLUMNS('regex'), *COLUMNS(['col1', 'col2'])
29285            // Check if * is followed by COLUMNS and (
29286            if self.check_next_identifier("COLUMNS") {
29287                // Check if there's a ( after COLUMNS
29288                if self
29289                    .tokens
29290                    .get(self.current + 2)
29291                    .map(|t| t.token_type == TokenType::LParen)
29292                    .unwrap_or(false)
29293                {
29294                    self.skip(); // consume *
29295                    self.skip(); // consume COLUMNS
29296                    self.skip(); // consume (
29297
29298                    // Parse the argument: can be *, a regex string, or an array of column names
29299                    let arg = if self.check(TokenType::Star) {
29300                        self.skip(); // consume *
29301                        Expression::Star(Star {
29302                            table: None,
29303                            except: None,
29304                            replace: None,
29305                            rename: None,
29306                            trailing_comments: Vec::new(),
29307                            span: None,
29308                        })
29309                    } else {
29310                        self.parse_expression()?
29311                    };
29312
29313                    self.expect(TokenType::RParen)?;
29314
29315                    // Create Columns expression with unpack=true
29316                    return Ok(Expression::Columns(Box::new(Columns {
29317                        this: Box::new(arg),
29318                        unpack: Some(Box::new(Expression::Boolean(BooleanLiteral {
29319                            value: true,
29320                        }))),
29321                    })));
29322                }
29323            }
29324
29325            // Regular star
29326            self.skip(); // consume *
29327            let star = self.parse_star_modifiers(None)?;
29328            return Ok(Expression::Star(star));
29329        }
29330
29331        // Generic type expressions: ARRAY<T>, MAP<K,V>, STRUCT<...>
29332        // These are standalone type expressions (not in CAST context)
29333        // But also handle STRUCT<TYPE>(args) which becomes CAST(STRUCT(args) AS STRUCT<TYPE>)
29334        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
29335            let name_upper = self.peek().text.to_ascii_uppercase();
29336            if (name_upper == "ARRAY" || name_upper == "MAP" || name_upper == "STRUCT")
29337                && self.check_next(TokenType::Lt)
29338            {
29339                self.skip(); // consume ARRAY/MAP/STRUCT
29340                let data_type = self.parse_data_type_from_name(&name_upper)?;
29341
29342                // Check for typed constructor: STRUCT<TYPE>(args) or ARRAY<TYPE>(args)
29343                // These become CAST(STRUCT(args) AS TYPE) or CAST(ARRAY(args) AS TYPE)
29344                if self.match_token(TokenType::LParen) {
29345                    if name_upper == "STRUCT" {
29346                        // Parse struct constructor arguments
29347                        let args = if self.check(TokenType::RParen) {
29348                            Vec::new()
29349                        } else {
29350                            self.parse_struct_args()?
29351                        };
29352                        self.expect(TokenType::RParen)?;
29353
29354                        // Convert args to Struct fields (all unnamed)
29355                        let fields: Vec<(Option<String>, Expression)> =
29356                            args.into_iter().map(|e| (None, e)).collect();
29357
29358                        // Create CAST(STRUCT(args) AS STRUCT<TYPE>)
29359                        let struct_expr = Expression::Struct(Box::new(Struct { fields }));
29360                        let cast_expr = Expression::Cast(Box::new(Cast {
29361                            this: struct_expr,
29362                            to: data_type,
29363                            trailing_comments: Vec::new(),
29364                            double_colon_syntax: false,
29365                            format: None,
29366                            default: None,
29367                            inferred_type: None,
29368                        }));
29369                        return self.maybe_parse_subscript(cast_expr);
29370                    } else if name_upper == "ARRAY" {
29371                        // Parse array constructor arguments
29372                        let mut expressions = Vec::new();
29373                        if !self.check(TokenType::RParen) {
29374                            loop {
29375                                expressions.push(self.parse_expression()?);
29376                                if !self.match_token(TokenType::Comma) {
29377                                    break;
29378                                }
29379                            }
29380                        }
29381                        self.expect(TokenType::RParen)?;
29382
29383                        // Create CAST(ARRAY[args] AS ARRAY<TYPE>)
29384                        let array_expr = Expression::Array(Box::new(Array { expressions }));
29385                        let cast_expr = Expression::Cast(Box::new(Cast {
29386                            this: array_expr,
29387                            to: data_type,
29388                            trailing_comments: Vec::new(),
29389                            double_colon_syntax: false,
29390                            format: None,
29391                            default: None,
29392                            inferred_type: None,
29393                        }));
29394                        return self.maybe_parse_subscript(cast_expr);
29395                    }
29396                } else if self.match_token(TokenType::LBracket) {
29397                    // ARRAY<TYPE>[values] or ARRAY<TYPE>[] - bracket-style array constructor
29398                    let expressions = if self.check(TokenType::RBracket) {
29399                        Vec::new()
29400                    } else {
29401                        self.parse_expression_list()?
29402                    };
29403                    self.expect(TokenType::RBracket)?;
29404                    // Create CAST(Array(values) AS DataType)
29405                    let array_expr = Expression::Array(Box::new(Array { expressions }));
29406                    let cast_expr = Expression::Cast(Box::new(Cast {
29407                        this: array_expr,
29408                        to: data_type,
29409                        trailing_comments: Vec::new(),
29410                        double_colon_syntax: false,
29411                        format: None,
29412                        default: None,
29413                        inferred_type: None,
29414                    }));
29415                    return self.maybe_parse_subscript(cast_expr);
29416                }
29417
29418                return Ok(Expression::DataType(data_type));
29419            }
29420            // DuckDB-style MAP with curly brace literals: MAP {'key': value}
29421            if name_upper == "MAP" && self.check_next(TokenType::LBrace) {
29422                self.skip(); // consume MAP
29423                self.expect(TokenType::LBrace)?;
29424
29425                // Handle empty: MAP {}
29426                if self.match_token(TokenType::RBrace) {
29427                    return self.maybe_parse_subscript(Expression::MapFunc(Box::new(
29428                        MapConstructor {
29429                            keys: Vec::new(),
29430                            values: Vec::new(),
29431                            curly_brace_syntax: true,
29432                            with_map_keyword: true,
29433                        },
29434                    )));
29435                }
29436
29437                // Parse key-value pairs
29438                let mut keys = Vec::new();
29439                let mut values = Vec::new();
29440                loop {
29441                    let key = self.parse_primary()?;
29442                    self.expect(TokenType::Colon)?;
29443                    let value = self.parse_expression()?;
29444                    keys.push(key);
29445                    values.push(value);
29446                    if !self.match_token(TokenType::Comma) {
29447                        break;
29448                    }
29449                    // Handle trailing comma
29450                    if self.check(TokenType::RBrace) {
29451                        break;
29452                    }
29453                }
29454                self.expect(TokenType::RBrace)?;
29455
29456                return self.maybe_parse_subscript(Expression::MapFunc(Box::new(MapConstructor {
29457                    keys,
29458                    values,
29459                    curly_brace_syntax: true,
29460                    with_map_keyword: true,
29461                })));
29462            }
29463        }
29464
29465        // Keywords as identifiers when followed by DOT (e.g., case.x, top.y)
29466        // These keywords can be table/column names when used with dot notation
29467        if (self.check(TokenType::Case) || self.check(TokenType::Top))
29468            && self.check_next(TokenType::Dot)
29469        {
29470            let token = self.advance();
29471            let ident = Identifier::new(token.text);
29472            self.expect(TokenType::Dot)?;
29473            if self.match_token(TokenType::Star) {
29474                // case.* or top.*
29475                let star = self.parse_star_modifiers(Some(ident))?;
29476                return Ok(Expression::Star(star));
29477            }
29478            // case.column or top.column
29479            let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
29480            // Capture trailing comments from the column name token
29481            let trailing_comments = self.previous_trailing_comments().to_vec();
29482            let mut col = Expression::boxed_column(Column {
29483                name: col_ident,
29484                table: Some(ident),
29485                join_mark: false,
29486                trailing_comments,
29487                span: None,
29488                inferred_type: None,
29489            });
29490            // Handle Oracle/Redshift outer join marker (+) after column reference
29491            if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
29492                let saved_pos = self.current;
29493                if self.match_token(TokenType::LParen)
29494                    && self.match_token(TokenType::Plus)
29495                    && self.match_token(TokenType::RParen)
29496                {
29497                    if let Expression::Column(ref mut c) = col {
29498                        c.join_mark = true;
29499                    }
29500                } else {
29501                    self.current = saved_pos;
29502                }
29503            }
29504            return self.maybe_parse_subscript(col);
29505        }
29506
29507        // MySQL BINARY prefix operator: BINARY expr -> CAST(expr AS BINARY)
29508        // Only treat as prefix operator when followed by an expression (not ( which would be BINARY() function,
29509        // and not when it would be a data type like BINARY in column definitions)
29510        if self.check(TokenType::Var)
29511            && self.peek().text.eq_ignore_ascii_case("BINARY")
29512            && !self.check_next(TokenType::LParen)
29513            && !self.check_next(TokenType::Dot)
29514            && !self.check_next(TokenType::RParen)
29515            && !self.check_next(TokenType::Comma)
29516            && !self.is_at_end()
29517        {
29518            // Check if this is actually followed by an expression token (not end of statement)
29519            let next_idx = self.current + 1;
29520            let has_expr = next_idx < self.tokens.len()
29521                && !matches!(
29522                    self.tokens[next_idx].token_type,
29523                    TokenType::Semicolon | TokenType::Eof | TokenType::RParen | TokenType::Comma
29524                );
29525            if has_expr {
29526                self.skip(); // consume BINARY
29527                let expr = self.parse_unary()?;
29528                return Ok(Expression::Cast(Box::new(Cast {
29529                    this: expr,
29530                    to: DataType::Binary { length: None },
29531                    trailing_comments: Vec::new(),
29532                    double_colon_syntax: false,
29533                    format: None,
29534                    default: None,
29535                    inferred_type: None,
29536                })));
29537            }
29538        }
29539
29540        // RLIKE/REGEXP as function call: RLIKE(expr, pattern, flags)
29541        // Normally RLIKE is an operator, but Snowflake allows function syntax
29542        if self.check(TokenType::RLike) && self.check_next(TokenType::LParen) {
29543            let token = self.advance(); // consume RLIKE
29544            self.skip(); // consume LParen
29545            let args = if self.check(TokenType::RParen) {
29546                Vec::new()
29547            } else {
29548                self.parse_function_arguments()?
29549            };
29550            self.expect(TokenType::RParen)?;
29551            let func = Expression::Function(Box::new(Function {
29552                name: token.text.clone(), // Preserve original case; generator handles normalization
29553                args,
29554                distinct: false,
29555                trailing_comments: Vec::new(),
29556                use_bracket_syntax: false,
29557                no_parens: false,
29558                quoted: false,
29559                span: None,
29560                inferred_type: None,
29561            }));
29562            return self.maybe_parse_over(func);
29563        }
29564
29565        // INSERT as function call: INSERT(str, pos, len, newstr)
29566        // Snowflake/MySQL have INSERT as a string function, but INSERT is also a DML keyword.
29567        // When followed by ( in expression context, treat as function call.
29568        if self.check(TokenType::Insert) && self.check_next(TokenType::LParen) {
29569            let token = self.advance(); // consume INSERT
29570            self.skip(); // consume LParen
29571            let args = if self.check(TokenType::RParen) {
29572                Vec::new()
29573            } else {
29574                self.parse_function_arguments()?
29575            };
29576            self.expect(TokenType::RParen)?;
29577            let func = Expression::Function(Box::new(Function {
29578                name: token.text.clone(),
29579                args,
29580                distinct: false,
29581                trailing_comments: Vec::new(),
29582                use_bracket_syntax: false,
29583                no_parens: false,
29584                quoted: false,
29585                span: None,
29586                inferred_type: None,
29587            }));
29588            return self.maybe_parse_over(func);
29589        }
29590
29591        // ClickHouse: MINUS/EXCEPT/INTERSECT/REGEXP as function names (e.g., minus(a, b), REGEXP('^db'))
29592        // MINUS is tokenized as TokenType::Except (Oracle alias), REGEXP as TokenType::RLike
29593        if matches!(
29594            self.config.dialect,
29595            Some(crate::dialects::DialectType::ClickHouse)
29596        ) && (self.check(TokenType::Except)
29597            || self.check(TokenType::Intersect)
29598            || self.check(TokenType::RLike))
29599            && self.check_next(TokenType::LParen)
29600        {
29601            let token = self.advance(); // consume keyword
29602            self.skip(); // consume LParen
29603            let args = if self.check(TokenType::RParen) {
29604                Vec::new()
29605            } else {
29606                self.parse_function_arguments()?
29607            };
29608            self.expect(TokenType::RParen)?;
29609            let func = Expression::Function(Box::new(Function {
29610                name: token.text.clone(),
29611                args,
29612                distinct: false,
29613                trailing_comments: Vec::new(),
29614                use_bracket_syntax: false,
29615                no_parens: false,
29616                quoted: false,
29617                span: None,
29618                inferred_type: None,
29619            }));
29620            return self.maybe_parse_over(func);
29621        }
29622
29623        // Handle CURRENT_DATE/CURRENT_TIMESTAMP/CURRENT_TIME/CURRENT_DATETIME with parentheses
29624        // These have special token types but BigQuery and others use them as function calls with args
29625        if matches!(
29626            self.peek().token_type,
29627            TokenType::CurrentDate
29628                | TokenType::CurrentTimestamp
29629                | TokenType::CurrentTime
29630                | TokenType::CurrentDateTime
29631        ) {
29632            // Snowflake: CURRENT_TIME / CURRENT_TIME(n) -> Localtime (so DuckDB can output LOCALTIME)
29633            if matches!(
29634                self.config.dialect,
29635                Some(crate::dialects::DialectType::Snowflake)
29636            ) && self.peek().token_type == TokenType::CurrentTime
29637            {
29638                self.skip(); // consume CURRENT_TIME
29639                if self.match_token(TokenType::LParen) {
29640                    // CURRENT_TIME(n) - consume args but ignore precision
29641                    if !self.check(TokenType::RParen) {
29642                        let _ = self.parse_function_arguments()?;
29643                    }
29644                    self.expect(TokenType::RParen)?;
29645                }
29646                return self.maybe_parse_subscript(Expression::Localtime(Box::new(
29647                    crate::expressions::Localtime { this: None },
29648                )));
29649            }
29650            if self.check_next(TokenType::LParen) {
29651                // Parse as function call: CURRENT_DATE('UTC'), CURRENT_TIMESTAMP(), etc.
29652                let token = self.advance(); // consume CURRENT_DATE etc.
29653                self.skip(); // consume LParen
29654                let args = if self.check(TokenType::RParen) {
29655                    Vec::new()
29656                } else {
29657                    self.parse_function_arguments()?
29658                };
29659                self.expect(TokenType::RParen)?;
29660                let func = Expression::Function(Box::new(Function {
29661                    name: token.text.clone(),
29662                    args,
29663                    distinct: false,
29664                    trailing_comments: Vec::new(),
29665                    use_bracket_syntax: false,
29666                    no_parens: false,
29667                    quoted: false,
29668                    span: None,
29669                    inferred_type: None,
29670                }));
29671                return self.maybe_parse_subscript(func);
29672            } else {
29673                // No parens - parse as no-paren function
29674                let token = self.advance();
29675                let func = Expression::Function(Box::new(Function {
29676                    name: token.text.clone(),
29677                    args: Vec::new(),
29678                    distinct: false,
29679                    trailing_comments: Vec::new(),
29680                    use_bracket_syntax: false,
29681                    no_parens: true,
29682                    quoted: false,
29683                    span: None,
29684                    inferred_type: None,
29685                }));
29686                return self.maybe_parse_subscript(func);
29687            }
29688        }
29689
29690        // Type keyword followed by string literal -> CAST('value' AS TYPE)
29691        // E.g., NUMERIC '2.25' -> CAST('2.25' AS NUMERIC)
29692        if self.is_identifier_token() && self.check_next(TokenType::String) {
29693            let upper_name = self.peek().text.to_ascii_uppercase();
29694            if matches!(
29695                upper_name.as_str(),
29696                "NUMERIC" | "DECIMAL" | "BIGNUMERIC" | "BIGDECIMAL"
29697            ) {
29698                self.skip(); // consume the type keyword
29699                let str_token = self.advance(); // consume the string literal
29700                let data_type = match upper_name.as_str() {
29701                    "NUMERIC" | "DECIMAL" | "BIGNUMERIC" | "BIGDECIMAL" => {
29702                        crate::expressions::DataType::Decimal {
29703                            precision: None,
29704                            scale: None,
29705                        }
29706                    }
29707                    _ => unreachable!("type keyword already matched in outer if-condition"),
29708                };
29709                return Ok(Expression::Cast(Box::new(crate::expressions::Cast {
29710                    this: Expression::Literal(Box::new(Literal::String(str_token.text))),
29711                    to: data_type,
29712                    trailing_comments: Vec::new(),
29713                    double_colon_syntax: false,
29714                    format: None,
29715                    default: None,
29716                    inferred_type: None,
29717                })));
29718            }
29719        }
29720
29721        // Identifier, Column, or Function
29722        if self.is_identifier_token() {
29723            // Check for no-paren functions like CURRENT_TIMESTAMP, CURRENT_DATE, etc.
29724            // These should be parsed as functions even without parentheses
29725            let upper_name = self.peek().text.to_ascii_uppercase();
29726            if !self.check_next(TokenType::LParen)
29727                && !self.check_next(TokenType::Dot)
29728                && crate::function_registry::is_no_paren_function_name_upper(upper_name.as_str())
29729                && !(matches!(
29730                    self.config.dialect,
29731                    Some(crate::dialects::DialectType::ClickHouse)
29732                ) && upper_name.as_str() == "CURRENT_TIMESTAMP")
29733            {
29734                let token = self.advance();
29735                let func = Expression::Function(Box::new(Function {
29736                    name: token.text.clone(), // Preserve original case; generator handles normalization
29737                    args: Vec::new(),
29738                    distinct: false,
29739                    trailing_comments: Vec::new(),
29740                    use_bracket_syntax: false,
29741                    no_parens: true, // These functions were called without parentheses
29742                    quoted: false,
29743                    span: None,
29744                    inferred_type: None,
29745                }));
29746                return self.maybe_parse_subscript(func);
29747            }
29748
29749            let ident = self.expect_identifier_with_quoted()?;
29750            let name = ident.name.clone();
29751            let quoted = ident.quoted;
29752
29753            // Check for function call (skip Teradata FORMAT phrase)
29754            let is_teradata_format_phrase = matches!(
29755                self.config.dialect,
29756                Some(crate::dialects::DialectType::Teradata)
29757            ) && self.check(TokenType::LParen)
29758                && self.check_next(TokenType::Format);
29759            if !is_teradata_format_phrase && self.match_token(TokenType::LParen) {
29760                let upper_name = name.to_ascii_uppercase();
29761                let func_expr = self.parse_typed_function(&name, &upper_name, quoted)?;
29762                let func_expr = self.maybe_parse_clickhouse_parameterized_agg(func_expr)?;
29763                // Check for OVER clause (window function)
29764                return self.maybe_parse_over(func_expr);
29765            }
29766
29767            // Check for qualified name (table.column or table.method())
29768            if self.match_token(TokenType::Dot) {
29769                if self.match_token(TokenType::Star) {
29770                    // table.* with potential modifiers
29771                    let star = self.parse_star_modifiers(Some(ident))?;
29772                    let mut star_expr = Expression::Star(star);
29773                    // ClickHouse: a.* APPLY(func) EXCEPT(col) REPLACE(expr AS col) in any order
29774                    if matches!(
29775                        self.config.dialect,
29776                        Some(crate::dialects::DialectType::ClickHouse)
29777                    ) {
29778                        loop {
29779                            if self.check(TokenType::Apply) {
29780                                self.skip();
29781                                let apply_expr = if self.match_token(TokenType::LParen) {
29782                                    let e = self.parse_expression()?;
29783                                    self.expect(TokenType::RParen)?;
29784                                    e
29785                                } else {
29786                                    self.parse_expression()?
29787                                };
29788                                star_expr =
29789                                    Expression::Apply(Box::new(crate::expressions::Apply {
29790                                        this: Box::new(star_expr),
29791                                        expression: Box::new(apply_expr),
29792                                    }));
29793                            } else if self.check(TokenType::Except)
29794                                || self.check(TokenType::Exclude)
29795                            {
29796                                self.skip();
29797                                self.match_identifier("STRICT");
29798                                if self.match_token(TokenType::LParen) {
29799                                    loop {
29800                                        if self.check(TokenType::RParen) {
29801                                            break;
29802                                        }
29803                                        let _ = self.parse_expression()?;
29804                                        if !self.match_token(TokenType::Comma) {
29805                                            break;
29806                                        }
29807                                    }
29808                                    self.expect(TokenType::RParen)?;
29809                                } else if self.is_identifier_token()
29810                                    || self.is_safe_keyword_as_identifier()
29811                                {
29812                                    let _ = self.parse_expression()?;
29813                                }
29814                            } else if self.check(TokenType::Replace) {
29815                                self.skip();
29816                                self.match_identifier("STRICT");
29817                                if self.match_token(TokenType::LParen) {
29818                                    loop {
29819                                        if self.check(TokenType::RParen) {
29820                                            break;
29821                                        }
29822                                        let _ = self.parse_expression()?;
29823                                        if self.match_token(TokenType::As) {
29824                                            if self.is_identifier_token()
29825                                                || self.is_safe_keyword_as_identifier()
29826                                            {
29827                                                self.skip();
29828                                            }
29829                                        }
29830                                        if !self.match_token(TokenType::Comma) {
29831                                            break;
29832                                        }
29833                                    }
29834                                    self.expect(TokenType::RParen)?;
29835                                } else {
29836                                    let _ = self.parse_expression()?;
29837                                    if self.match_token(TokenType::As) {
29838                                        if self.is_identifier_token()
29839                                            || self.is_safe_keyword_as_identifier()
29840                                        {
29841                                            self.skip();
29842                                        }
29843                                    }
29844                                }
29845                            } else {
29846                                break;
29847                            }
29848                        }
29849                    }
29850                    return Ok(star_expr);
29851                }
29852                // Handle numeric field access: a.1, t.2 (ClickHouse tuple field access)
29853                // Also handle negative: a.-1 (ClickHouse negative tuple index)
29854                if self.check(TokenType::Number) {
29855                    let field_name = self.advance().text;
29856                    let col_expr = Expression::Dot(Box::new(DotAccess {
29857                        this: Expression::boxed_column(Column {
29858                            name: ident,
29859                            table: None,
29860                            join_mark: false,
29861                            trailing_comments: Vec::new(),
29862                            span: None,
29863                            inferred_type: None,
29864                        }),
29865                        field: Identifier::new(field_name),
29866                    }));
29867                    return self.maybe_parse_subscript(col_expr);
29868                }
29869                if matches!(
29870                    self.config.dialect,
29871                    Some(crate::dialects::DialectType::ClickHouse)
29872                ) && self.check(TokenType::Dash)
29873                    && self.current + 1 < self.tokens.len()
29874                    && self.tokens[self.current + 1].token_type == TokenType::Number
29875                {
29876                    self.skip(); // consume -
29877                    let num = self.advance().text;
29878                    let field_name = format!("-{}", num);
29879                    let col_expr = Expression::Dot(Box::new(DotAccess {
29880                        this: Expression::boxed_column(Column {
29881                            name: ident,
29882                            table: None,
29883                            join_mark: false,
29884                            trailing_comments: Vec::new(),
29885                            span: None,
29886                            inferred_type: None,
29887                        }),
29888                        field: Identifier::new(field_name),
29889                    }));
29890                    return self.maybe_parse_subscript(col_expr);
29891                }
29892                // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
29893                if matches!(
29894                    self.config.dialect,
29895                    Some(crate::dialects::DialectType::ClickHouse)
29896                ) && self.check(TokenType::Caret)
29897                {
29898                    self.skip(); // consume ^
29899                    let mut field_name = "^".to_string();
29900                    if self.check(TokenType::Identifier)
29901                        || self.check(TokenType::Var)
29902                        || self.check_keyword()
29903                    {
29904                        field_name.push_str(&self.advance().text);
29905                    }
29906                    let col_expr = Expression::Dot(Box::new(DotAccess {
29907                        this: Expression::boxed_column(Column {
29908                            name: ident,
29909                            table: None,
29910                            join_mark: false,
29911                            trailing_comments: Vec::new(),
29912                            span: None,
29913                            inferred_type: None,
29914                        }),
29915                        field: Identifier::new(field_name),
29916                    }));
29917                    return self.maybe_parse_subscript(col_expr);
29918                }
29919                // Allow keywords as column names (e.g., a.filter, x.update)
29920                let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
29921
29922                // Handle Oracle/Redshift outer join marker (+) BEFORE checking for method call
29923                // This is critical: (+) looks like a method call but is actually a join marker
29924                if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
29925                    let saved_pos = self.current;
29926                    if self.match_token(TokenType::LParen)
29927                        && self.match_token(TokenType::Plus)
29928                        && self.match_token(TokenType::RParen)
29929                    {
29930                        let trailing_comments = self.previous_trailing_comments().to_vec();
29931                        let col = Expression::boxed_column(Column {
29932                            name: col_ident,
29933                            table: Some(ident),
29934                            join_mark: true,
29935                            trailing_comments,
29936                            span: None,
29937                            inferred_type: None,
29938                        });
29939                        return self.maybe_parse_subscript(col);
29940                    } else {
29941                        self.current = saved_pos;
29942                    }
29943                }
29944
29945                // Check if this is a method call (column followed by parentheses)
29946                if self.check(TokenType::LParen) {
29947                    // This is a method call like table.EXTRACT() or obj.INT()
29948                    self.skip(); // consume (
29949                    let args = if self.check(TokenType::RParen) {
29950                        Vec::new()
29951                    } else {
29952                        self.parse_expression_list()?
29953                    };
29954                    self.expect(TokenType::RParen)?;
29955                    let method_call = Expression::MethodCall(Box::new(MethodCall {
29956                        this: Expression::boxed_column(Column {
29957                            name: ident.clone(),
29958                            table: None,
29959                            join_mark: false,
29960                            trailing_comments: Vec::new(),
29961                            span: None,
29962                            inferred_type: None,
29963                        }),
29964                        method: col_ident,
29965                        args,
29966                    }));
29967                    return self.maybe_parse_subscript(method_call);
29968                }
29969
29970                // Capture trailing comments from the column name token
29971                let trailing_comments = self.previous_trailing_comments().to_vec();
29972                let col = Expression::boxed_column(Column {
29973                    name: col_ident,
29974                    table: Some(ident),
29975                    join_mark: false,
29976                    trailing_comments,
29977                    span: None,
29978                    inferred_type: None,
29979                });
29980                return self.maybe_parse_subscript(col);
29981            }
29982
29983            // Check for Oracle pseudocolumns (ROWNUM, ROWID, LEVEL, SYSDATE, etc.)
29984            // Oracle pseudocolumns (LEVEL, ROWNUM, ROWID, SYSDATE, etc.)
29985            // Only recognize in Oracle and generic dialect — other dialects treat these as regular identifiers
29986            if !quoted
29987                && matches!(
29988                    self.config.dialect,
29989                    Some(crate::dialects::DialectType::Oracle) | None
29990                )
29991            {
29992                if let Some(pseudocolumn_type) = PseudocolumnType::from_str(&name) {
29993                    return Ok(Expression::Pseudocolumn(Pseudocolumn {
29994                        kind: pseudocolumn_type,
29995                    }));
29996                }
29997            }
29998
29999            // Check for lambda expression: x -> body
30000            // But NOT if followed by a string literal (that's JSON extract: col -> '$.path')
30001            if self.check(TokenType::Arrow)
30002                && !self
30003                    .peek_nth(1)
30004                    .map_or(false, |t| t.token_type == TokenType::String)
30005            {
30006                self.skip(); // consume the Arrow token
30007                let body = self.parse_expression()?;
30008                return Ok(Expression::Lambda(Box::new(LambdaExpr {
30009                    parameters: vec![ident],
30010                    body,
30011                    colon: false,
30012                    parameter_types: Vec::new(),
30013                })));
30014            }
30015
30016            // Capture trailing comments from the identifier token
30017            let trailing_comments = self.previous_trailing_comments().to_vec();
30018            let col = Expression::boxed_column(Column {
30019                name: ident,
30020                table: None,
30021                join_mark: false,
30022                trailing_comments,
30023                span: None,
30024                inferred_type: None,
30025            });
30026            return self.maybe_parse_subscript(col);
30027        }
30028
30029        // Exasol-style IF expression: IF condition THEN true_value ELSE false_value ENDIF
30030        // Check for IF not followed by ( (which would be IF function call handled elsewhere)
30031        // This handles: IF age < 18 THEN 'minor' ELSE 'adult' ENDIF
30032        // IMPORTANT: This must be checked BEFORE is_safe_keyword_as_identifier() which would
30033        // treat IF as a column name when not followed by ( or .
30034        // For TSQL/Fabric: IF (cond) BEGIN ... END is an IF statement, not function
30035        if self.check(TokenType::If)
30036            && !self.check_next(TokenType::Dot)
30037            && (!self.check_next(TokenType::LParen)
30038                || matches!(
30039                    self.config.dialect,
30040                    Some(crate::dialects::DialectType::TSQL)
30041                        | Some(crate::dialects::DialectType::Fabric)
30042                ))
30043        {
30044            let saved_pos = self.current;
30045            self.skip(); // consume IF
30046            if let Some(if_expr) = self.parse_if()? {
30047                return Ok(if_expr);
30048            }
30049            // parse_if() returned None — IF is not an IF expression here,
30050            // restore position so it can be treated as an identifier
30051            self.current = saved_pos;
30052        }
30053
30054        // NEXT VALUE FOR sequence_name [OVER (ORDER BY ...)]
30055        // Must check before treating NEXT as a standalone identifier via is_safe_keyword_as_identifier
30056        if self.check(TokenType::Next)
30057            && self.current + 2 < self.tokens.len()
30058            && self.tokens[self.current + 1]
30059                .text
30060                .eq_ignore_ascii_case("VALUE")
30061            && self.tokens[self.current + 2]
30062                .text
30063                .eq_ignore_ascii_case("FOR")
30064        {
30065            self.skip(); // consume NEXT
30066            if let Some(expr) = self.parse_next_value_for()? {
30067                return Ok(expr);
30068            }
30069        }
30070
30071        // ClickHouse: `from` can be a column name when followed by comma or dot
30072        if matches!(
30073            self.config.dialect,
30074            Some(crate::dialects::DialectType::ClickHouse)
30075        ) && self.check(TokenType::From)
30076            && (self.check_next(TokenType::Comma) || self.check_next(TokenType::Dot))
30077        {
30078            let token = self.advance();
30079            let name = token.text.clone();
30080            if self.match_token(TokenType::Dot) {
30081                // from.col qualified reference
30082                let col_name = self.expect_identifier_or_keyword()?;
30083                return Ok(Expression::Column(Box::new(crate::expressions::Column {
30084                    name: Identifier::new(col_name),
30085                    table: Some(Identifier::new(name)),
30086                    join_mark: false,
30087                    trailing_comments: Vec::new(),
30088                    span: None,
30089                    inferred_type: None,
30090                })));
30091            }
30092            return Ok(Expression::Column(Box::new(crate::expressions::Column {
30093                name: Identifier::new(name),
30094                table: None,
30095                join_mark: false,
30096                trailing_comments: Vec::new(),
30097                span: None,
30098                inferred_type: None,
30099            })));
30100        }
30101
30102        // ClickHouse: `except` as identifier in expression context (set operations are handled at statement level)
30103        // except(args) is already handled above in the MINUS/EXCEPT/INTERSECT function block
30104        if matches!(
30105            self.config.dialect,
30106            Some(crate::dialects::DialectType::ClickHouse)
30107        ) && self.check(TokenType::Except)
30108            && !self.check_next(TokenType::LParen)
30109        {
30110            let token = self.advance();
30111            let name = token.text.clone();
30112            if self.match_token(TokenType::Dot) {
30113                let col_name = self.expect_identifier_or_keyword()?;
30114                return Ok(Expression::Column(Box::new(crate::expressions::Column {
30115                    name: Identifier::new(col_name),
30116                    table: Some(Identifier::new(name)),
30117                    join_mark: false,
30118                    trailing_comments: Vec::new(),
30119                    span: None,
30120                    inferred_type: None,
30121                })));
30122            }
30123            return Ok(Expression::Column(Box::new(crate::expressions::Column {
30124                name: Identifier::new(name),
30125                table: None,
30126                join_mark: false,
30127                trailing_comments: Vec::new(),
30128                span: None,
30129                inferred_type: None,
30130            })));
30131        }
30132
30133        // ClickHouse: structural keywords like FROM, ON, JOIN can be used as identifiers
30134        // in expression context when followed by an operator (e.g., from + 1, on.col)
30135        if matches!(
30136            self.config.dialect,
30137            Some(crate::dialects::DialectType::ClickHouse)
30138        ) && self.peek().token_type.is_keyword()
30139            && !self.is_safe_keyword_as_identifier()
30140        {
30141            let next_tt = self
30142                .peek_nth(1)
30143                .map(|t| t.token_type)
30144                .unwrap_or(TokenType::Semicolon);
30145            // A structural keyword can be used as an identifier when it appears
30146            // in expression context. We detect this by checking what follows.
30147            // Essentially: it's NOT an identifier only if the keyword itself starts
30148            // a clause (e.g., FROM followed by a table name). But when it's followed
30149            // by an operator, comma, close-paren, or even another clause keyword
30150            // (meaning it's the last token in an expression), it's an identifier.
30151            let is_expr_context = !matches!(
30152                next_tt,
30153                TokenType::Identifier
30154                    | TokenType::Var
30155                    | TokenType::QuotedIdentifier
30156                    | TokenType::LParen
30157                    | TokenType::Number
30158                    | TokenType::String
30159            );
30160            if is_expr_context {
30161                let token = self.advance();
30162                return Ok(Expression::boxed_column(Column {
30163                    name: Identifier::new(token.text),
30164                    table: None,
30165                    join_mark: false,
30166                    trailing_comments: Vec::new(),
30167                    span: None,
30168                    inferred_type: None,
30169                }));
30170            }
30171        }
30172        // %s or %(name)s percent parameter (PostgreSQL psycopg2 style)
30173        // Must be checked BEFORE the keyword-as-identifier handler below, since
30174        // Percent is in is_keyword() and is_safe_keyword_as_identifier() returns true for it.
30175        if self.check(TokenType::Percent)
30176            && (
30177                self.check_next(TokenType::Var)  // %s
30178            || self.check_next(TokenType::LParen)
30179                // %(name)s
30180            )
30181        {
30182            self.skip(); // consume %
30183                         // Check for %(name)s - named parameter
30184            if self.match_token(TokenType::LParen) {
30185                // Get the parameter name
30186                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30187                    let name = self.advance().text;
30188                    self.expect(TokenType::RParen)?;
30189                    // Expect 's' after the closing paren
30190                    if self.check(TokenType::Var) && self.peek().text == "s" {
30191                        self.skip(); // consume 's'
30192                    }
30193                    return Ok(Expression::Parameter(Box::new(Parameter {
30194                        name: Some(name),
30195                        index: None,
30196                        style: ParameterStyle::Percent,
30197                        quoted: false,
30198                        string_quoted: false,
30199                        expression: None,
30200                    })));
30201                } else {
30202                    return Err(self.parse_error("Expected parameter name after %("));
30203                }
30204            }
30205            // Check for %s - anonymous parameter
30206            if self.check(TokenType::Var) && self.peek().text == "s" {
30207                self.skip(); // consume 's'
30208                return Ok(Expression::Parameter(Box::new(Parameter {
30209                    name: None,
30210                    index: None,
30211                    style: ParameterStyle::Percent,
30212                    quoted: false,
30213                    string_quoted: false,
30214                    expression: None,
30215                })));
30216            }
30217            // Not a parameter - backtrack
30218            self.current -= 1;
30219        }
30220
30221        // Some keywords can be used as identifiers (column names, table names, etc.)
30222        // when they are "safe" keywords that don't affect query structure.
30223        // Structural keywords like FROM, WHERE, JOIN should NOT be usable as identifiers.
30224        if self.is_safe_keyword_as_identifier() {
30225            let token = self.advance();
30226            let name = token.text.clone();
30227
30228            // Check for function call (keyword followed by paren) - skip Teradata FORMAT phrase
30229            let is_teradata_format_phrase = matches!(
30230                self.config.dialect,
30231                Some(crate::dialects::DialectType::Teradata)
30232            ) && self.check(TokenType::LParen)
30233                && self.check_next(TokenType::Format);
30234            if !is_teradata_format_phrase && self.match_token(TokenType::LParen) {
30235                let upper_name = name.to_ascii_uppercase();
30236                let func_expr = self.parse_typed_function(&name, &upper_name, false)?;
30237                let func_expr = self.maybe_parse_clickhouse_parameterized_agg(func_expr)?;
30238                return self.maybe_parse_over(func_expr);
30239            }
30240
30241            // Check for qualified name (keyword.column or keyword.method())
30242            if self.match_token(TokenType::Dot) {
30243                if self.match_token(TokenType::Star) {
30244                    // keyword.* with potential modifiers
30245                    let ident = Identifier::new(name);
30246                    let star = self.parse_star_modifiers(Some(ident))?;
30247                    return Ok(Expression::Star(star));
30248                }
30249                // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
30250                if matches!(
30251                    self.config.dialect,
30252                    Some(crate::dialects::DialectType::ClickHouse)
30253                ) && self.check(TokenType::Caret)
30254                {
30255                    self.skip(); // consume ^
30256                    let mut field_name = "^".to_string();
30257                    if self.check(TokenType::Identifier)
30258                        || self.check(TokenType::Var)
30259                        || self.check_keyword()
30260                    {
30261                        field_name.push_str(&self.advance().text);
30262                    }
30263                    let col = Expression::Dot(Box::new(DotAccess {
30264                        this: Expression::boxed_column(Column {
30265                            name: Identifier::new(name),
30266                            table: None,
30267                            join_mark: false,
30268                            trailing_comments: Vec::new(),
30269                            span: None,
30270                            inferred_type: None,
30271                        }),
30272                        field: Identifier::new(field_name),
30273                    }));
30274                    return self.maybe_parse_subscript(col);
30275                }
30276
30277                // Handle numeric field access: keyword.1, keyword.2 (ClickHouse tuple field access)
30278                if self.check(TokenType::Number) {
30279                    let field_name = self.advance().text;
30280                    let col_expr = Expression::Dot(Box::new(DotAccess {
30281                        this: Expression::boxed_column(Column {
30282                            name: Identifier::new(name),
30283                            table: None,
30284                            join_mark: false,
30285                            trailing_comments: Vec::new(),
30286                            span: None,
30287                            inferred_type: None,
30288                        }),
30289                        field: Identifier::new(field_name),
30290                    }));
30291                    return self.maybe_parse_subscript(col_expr);
30292                }
30293
30294                // Allow keywords as column names
30295                let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
30296
30297                // Check if this is a method call
30298                if self.check(TokenType::LParen) {
30299                    self.skip(); // consume (
30300                    let args = if self.check(TokenType::RParen) {
30301                        Vec::new()
30302                    } else {
30303                        self.parse_expression_list()?
30304                    };
30305                    self.expect(TokenType::RParen)?;
30306                    let method_call = Expression::MethodCall(Box::new(MethodCall {
30307                        this: Expression::Identifier(Identifier::new(name)),
30308                        method: col_ident,
30309                        args,
30310                    }));
30311                    return self.maybe_parse_subscript(method_call);
30312                }
30313
30314                // Capture trailing comments from the column name token
30315                let trailing_comments = self.previous_trailing_comments().to_vec();
30316                let mut col = Expression::boxed_column(Column {
30317                    name: col_ident,
30318                    table: Some(Identifier::new(name)),
30319                    join_mark: false,
30320                    trailing_comments,
30321                    span: None,
30322                    inferred_type: None,
30323                });
30324                // Handle Oracle/Redshift outer join marker (+) after column reference
30325                if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
30326                    let saved_pos = self.current;
30327                    if self.match_token(TokenType::LParen)
30328                        && self.match_token(TokenType::Plus)
30329                        && self.match_token(TokenType::RParen)
30330                    {
30331                        if let Expression::Column(ref mut c) = col {
30332                            c.join_mark = true;
30333                        }
30334                    } else {
30335                        self.current = saved_pos;
30336                    }
30337                }
30338                return self.maybe_parse_subscript(col);
30339            }
30340
30341            // Simple identifier (keyword used as column name)
30342            // Capture trailing comments from the keyword token
30343            let trailing_comments = self.previous_trailing_comments().to_vec();
30344            let ident = Identifier::new(name);
30345            let col = Expression::boxed_column(Column {
30346                name: ident,
30347                table: None,
30348                join_mark: false,
30349                trailing_comments,
30350                span: None,
30351                inferred_type: None,
30352            });
30353            return self.maybe_parse_subscript(col);
30354        }
30355
30356        // @@ system variable (MySQL/SQL Server): @@version, @@IDENTITY, @@GLOBAL.var
30357        if self.match_token(TokenType::AtAt) {
30358            // Get the variable name
30359            let name = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
30360                let mut n = self.advance().text;
30361                // Handle @@scope.variable (e.g., @@GLOBAL.max_connections, @@SESSION.sql_mode)
30362                if self.match_token(TokenType::Dot) {
30363                    if self.check(TokenType::Identifier)
30364                        || self.check(TokenType::Var)
30365                        || self.is_safe_keyword_as_identifier()
30366                    {
30367                        n.push('.');
30368                        n.push_str(&self.advance().text);
30369                    }
30370                }
30371                n
30372            } else if self.check_keyword() {
30373                // Handle @@keyword (e.g., @@sql_mode when sql_mode is a keyword)
30374                self.advance().text
30375            } else {
30376                return Err(self.parse_error("Expected variable name after @@"));
30377            };
30378            return Ok(Expression::Parameter(Box::new(Parameter {
30379                name: Some(name),
30380                index: None,
30381                style: ParameterStyle::DoubleAt,
30382                quoted: false,
30383                string_quoted: false,
30384                expression: None,
30385            })));
30386        }
30387
30388        // @ user variable/parameter: @x, @"x", @JOIN, @'foo'
30389        if self.match_token(TokenType::DAt) {
30390            // Get the variable name - can be identifier, quoted identifier, keyword, or string
30391            let (name, quoted, string_quoted) =
30392                if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
30393                    (self.advance().text, false, false)
30394                } else if self.check(TokenType::QuotedIdentifier) {
30395                    // Quoted identifier like @"x"
30396                    let token = self.advance();
30397                    (token.text, true, false)
30398                } else if self.check(TokenType::String) {
30399                    // String-quoted like @'foo'
30400                    let token = self.advance();
30401                    (token.text, false, true)
30402                } else if self.check(TokenType::Number) {
30403                    // Numeric like @1
30404                    let token = self.advance();
30405                    (token.text, false, false)
30406                } else if self.peek().token_type.is_keyword() {
30407                    // Keyword used as variable name like @JOIN
30408                    let token = self.advance();
30409                    (token.text, false, false)
30410                } else {
30411                    return Err(self.parse_error("Expected variable name after @"));
30412                };
30413            return Ok(Expression::Parameter(Box::new(Parameter {
30414                name: Some(name),
30415                index: None,
30416                style: ParameterStyle::At,
30417                quoted,
30418                string_quoted,
30419                expression: None,
30420            })));
30421        }
30422
30423        // Parameter: ? placeholder or $n positional parameter
30424        if self.check(TokenType::Parameter) {
30425            let token = self.advance();
30426            // Check if this is a positional parameter ($1, $2, etc.) or a plain ? placeholder
30427            if let Ok(index) = token.text.parse::<u32>() {
30428                // Positional parameter like $1, $2 (token text is just the number)
30429                let param = Expression::Parameter(Box::new(Parameter {
30430                    name: None,
30431                    index: Some(index),
30432                    style: ParameterStyle::Dollar,
30433                    quoted: false,
30434                    string_quoted: false,
30435                    expression: None,
30436                }));
30437                // Check for JSON path access: $1:name or dot access: $1.c1
30438                let result = self.parse_colon_json_path(param)?;
30439                return self.maybe_parse_subscript(result);
30440            } else {
30441                // Plain ? placeholder
30442                return Ok(Expression::Placeholder(Placeholder { index: None }));
30443            }
30444        }
30445
30446        // :name or :1 colon parameter
30447        if self.match_token(TokenType::Colon) {
30448            // Check for numeric parameter :1, :2, etc.
30449            if self.check(TokenType::Number) {
30450                let num_token = self.advance();
30451                if let Ok(index) = num_token.text.parse::<u32>() {
30452                    return Ok(Expression::Parameter(Box::new(Parameter {
30453                        name: None,
30454                        index: Some(index),
30455                        style: ParameterStyle::Colon,
30456                        quoted: false,
30457                        string_quoted: false,
30458                        expression: None,
30459                    })));
30460                }
30461                return Err(
30462                    self.parse_error(format!("Invalid colon parameter: :{}", num_token.text))
30463                );
30464            }
30465            // Get the parameter name
30466            if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30467                let name = self.advance().text;
30468                return Ok(Expression::Parameter(Box::new(Parameter {
30469                    name: Some(name),
30470                    index: None,
30471                    style: ParameterStyle::Colon,
30472                    quoted: false,
30473                    string_quoted: false,
30474                    expression: None,
30475                })));
30476            } else {
30477                return Err(self.parse_error("Expected parameter name after :"));
30478            }
30479        }
30480
30481        // $n dollar parameter: $1, $2, etc.
30482        if self.match_token(TokenType::Dollar) {
30483            // Check for ${identifier} or ${kind:name} template variable syntax (Databricks, Hive)
30484            // Hive supports ${hiveconf:variable_name} syntax
30485            if self.match_token(TokenType::LBrace) {
30486                // Parse the variable name - can be identifier or keyword
30487                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30488                    let name_token = self.advance();
30489                    // Check for ${kind:name} syntax (e.g., ${hiveconf:some_var})
30490                    let expression = if self.match_token(TokenType::Colon) {
30491                        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30492                            let expr_token = self.advance();
30493                            Some(expr_token.text.clone())
30494                        } else {
30495                            return Err(self.parse_error("Expected identifier after : in ${...}"));
30496                        }
30497                    } else {
30498                        None
30499                    };
30500                    self.expect(TokenType::RBrace)?;
30501                    return Ok(Expression::Parameter(Box::new(Parameter {
30502                        name: Some(name_token.text.clone()),
30503                        index: None,
30504                        style: ParameterStyle::DollarBrace,
30505                        quoted: false,
30506                        string_quoted: false,
30507                        expression,
30508                    })));
30509                } else {
30510                    return Err(self.parse_error("Expected identifier after ${"));
30511                }
30512            }
30513            // Check for number following the dollar sign → positional parameter ($1, $2, etc.)
30514            if self.check(TokenType::Number) {
30515                let num_token = self.advance();
30516                // Parse the number as an index
30517                if let Ok(index) = num_token.text.parse::<u32>() {
30518                    let param_expr = Expression::Parameter(Box::new(Parameter {
30519                        name: None,
30520                        index: Some(index),
30521                        style: ParameterStyle::Dollar,
30522                        quoted: false,
30523                        string_quoted: false,
30524                        expression: None,
30525                    }));
30526                    // Check for JSON path access: $1:name or $1:name:subname
30527                    let result = self.parse_colon_json_path(param_expr)?;
30528                    // Also check for dot access: $1.c1 or $1:name.field
30529                    return self.maybe_parse_subscript(result);
30530                }
30531                // If it's not a valid integer, treat as error
30532                return Err(
30533                    self.parse_error(format!("Invalid dollar parameter: ${}", num_token.text))
30534                );
30535            }
30536            // Check for identifier following the dollar sign → session variable ($x, $query_id, etc.)
30537            if self.check(TokenType::Identifier)
30538                || self.check(TokenType::Var)
30539                || self.is_safe_keyword_as_identifier()
30540            {
30541                let name_token = self.advance();
30542                return Ok(Expression::Parameter(Box::new(Parameter {
30543                    name: Some(name_token.text.clone()),
30544                    index: None,
30545                    style: ParameterStyle::Dollar,
30546                    quoted: false,
30547                    string_quoted: false,
30548                    expression: None,
30549                })));
30550            }
30551            // Just a $ by itself - treat as error
30552            return Err(self.parse_error("Expected number or identifier after $"));
30553        }
30554
30555        // %s or %(name)s percent parameter (PostgreSQL psycopg2 style)
30556        if self.match_token(TokenType::Percent) {
30557            // Check for %(name)s - named parameter
30558            if self.match_token(TokenType::LParen) {
30559                // Get the parameter name
30560                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
30561                    let name = self.advance().text;
30562                    self.expect(TokenType::RParen)?;
30563                    // Expect 's' after the closing paren
30564                    if self.check(TokenType::Var) && self.peek().text == "s" {
30565                        self.skip(); // consume 's'
30566                    }
30567                    return Ok(Expression::Parameter(Box::new(Parameter {
30568                        name: Some(name),
30569                        index: None,
30570                        style: ParameterStyle::Percent,
30571                        quoted: false,
30572                        string_quoted: false,
30573                        expression: None,
30574                    })));
30575                } else {
30576                    return Err(self.parse_error("Expected parameter name after %("));
30577                }
30578            }
30579            // Check for %s - anonymous parameter
30580            if self.check(TokenType::Var) && self.peek().text == "s" {
30581                self.skip(); // consume 's'
30582                return Ok(Expression::Parameter(Box::new(Parameter {
30583                    name: None,
30584                    index: None,
30585                    style: ParameterStyle::Percent,
30586                    quoted: false,
30587                    string_quoted: false,
30588                    expression: None,
30589                })));
30590            }
30591            // If not followed by 's' or '(', it's not a parameter - error
30592            return Err(self.parse_error("Expected 's' or '(' after % for parameter"));
30593        }
30594
30595        // LEFT, RIGHT, OUTER, FULL, ALL etc. keywords as identifiers when followed by DOT
30596        // e.g., SELECT LEFT.FOO FROM ... or SELECT all.count FROM ...
30597        if (self.check(TokenType::Left)
30598            || self.check(TokenType::Right)
30599            || self.check(TokenType::Outer)
30600            || self.check(TokenType::Full)
30601            || self.check(TokenType::All)
30602            || self.check(TokenType::Only)
30603            || self.check(TokenType::Next)
30604            || self.check(TokenType::If))
30605            && self.check_next(TokenType::Dot)
30606        {
30607            let token = self.advance();
30608            let ident = Identifier::new(token.text);
30609            self.expect(TokenType::Dot)?;
30610            if self.match_token(TokenType::Star) {
30611                let star = self.parse_star_modifiers(Some(ident))?;
30612                return Ok(Expression::Star(star));
30613            }
30614            let col_ident = self.expect_identifier_or_keyword_with_quoted()?;
30615            let trailing_comments = self.previous_trailing_comments().to_vec();
30616            let mut col = Expression::boxed_column(Column {
30617                name: col_ident,
30618                table: Some(ident),
30619                join_mark: false,
30620                trailing_comments,
30621                span: None,
30622                inferred_type: None,
30623            });
30624            // Handle Oracle/Redshift outer join marker (+) after column reference
30625            if self.check(TokenType::LParen) && self.check_next(TokenType::Plus) {
30626                let saved_pos = self.current;
30627                if self.match_token(TokenType::LParen)
30628                    && self.match_token(TokenType::Plus)
30629                    && self.match_token(TokenType::RParen)
30630                {
30631                    if let Expression::Column(ref mut c) = col {
30632                        c.join_mark = true;
30633                    }
30634                } else {
30635                    self.current = saved_pos;
30636                }
30637            }
30638            return self.maybe_parse_subscript(col);
30639        }
30640
30641        // NEXT VALUE FOR sequence_name [OVER (ORDER BY ...)]
30642        // Must check before treating NEXT as a standalone identifier
30643        if self.check(TokenType::Next) {
30644            // NEXT(arg) - pattern navigation function in MATCH_RECOGNIZE
30645            if self.check_next(TokenType::LParen) {
30646                let token = self.advance();
30647                self.skip(); // consume LParen
30648                let args = self.parse_function_args_list()?;
30649                self.expect(TokenType::RParen)?;
30650                return Ok(Expression::Function(Box::new(Function {
30651                    name: token.text,
30652                    args,
30653                    distinct: false,
30654                    trailing_comments: Vec::new(),
30655                    use_bracket_syntax: false,
30656                    no_parens: false,
30657                    quoted: false,
30658                    span: None,
30659                    inferred_type: None,
30660                })));
30661            }
30662        }
30663
30664        // LEFT, RIGHT, OUTER, FULL, ONLY, NEXT as standalone identifiers (not followed by JOIN or LParen)
30665        // e.g., SELECT LEFT FROM ... or SELECT only FROM ...
30666        // If followed by LParen, it's a function call (e.g., NEXT(bar) in MATCH_RECOGNIZE)
30667        if self.can_be_alias_keyword()
30668            && !self.check_next(TokenType::Join)
30669            && !self.check_next(TokenType::LParen)
30670        {
30671            let token = self.advance();
30672            let trailing_comments = self.previous_trailing_comments().to_vec();
30673            let col = Expression::boxed_column(Column {
30674                name: Identifier::new(token.text),
30675                table: None,
30676                join_mark: false,
30677                trailing_comments,
30678                span: None,
30679                inferred_type: None,
30680            });
30681            return self.maybe_parse_subscript(col);
30682        }
30683
30684        Err(self.parse_error(format!("Unexpected token: {:?}", self.peek().token_type)))
30685    }
30686
30687    /// Check if function name is a known aggregate function
30688    fn is_aggregate_function(name: &str) -> bool {
30689        crate::function_registry::is_aggregate_function_name(name)
30690    }
30691
30692    /// Whether the source dialect uses LOG(base, value) order (base first).
30693    /// Default is true. BigQuery, TSQL, Tableau, Fabric use LOG(value, base).
30694    fn log_base_first(&self) -> bool {
30695        !matches!(
30696            self.config.dialect,
30697            Some(crate::dialects::DialectType::BigQuery)
30698                | Some(crate::dialects::DialectType::TSQL)
30699                | Some(crate::dialects::DialectType::Tableau)
30700                | Some(crate::dialects::DialectType::Fabric)
30701        )
30702    }
30703
30704    /// Whether the source dialect treats single-arg LOG(x) as LN(x).
30705    /// These dialects have LOG_DEFAULTS_TO_LN = True in Python sqlglot.
30706    fn log_defaults_to_ln(&self) -> bool {
30707        matches!(
30708            self.config.dialect,
30709            Some(crate::dialects::DialectType::MySQL)
30710                | Some(crate::dialects::DialectType::BigQuery)
30711                | Some(crate::dialects::DialectType::TSQL)
30712                | Some(crate::dialects::DialectType::ClickHouse)
30713                | Some(crate::dialects::DialectType::Hive)
30714                | Some(crate::dialects::DialectType::Spark)
30715                | Some(crate::dialects::DialectType::Databricks)
30716                | Some(crate::dialects::DialectType::Drill)
30717                | Some(crate::dialects::DialectType::Dremio)
30718        )
30719    }
30720
30721    /// Parse the subset of typed functions that are handled via function-registry metadata.
30722    fn try_parse_registry_typed_function(
30723        &mut self,
30724        name: &str,
30725        upper_name: &str,
30726        canonical_upper_name: &str,
30727        quoted: bool,
30728    ) -> Result<Option<Expression>> {
30729        let Some(spec) =
30730            crate::function_registry::typed_function_spec_by_canonical_upper(canonical_upper_name)
30731        else {
30732            return Ok(None);
30733        };
30734
30735        match (spec.parse_kind, spec.canonical_name) {
30736            (crate::function_registry::TypedParseKind::AggregateLike, "COUNT_IF") => {
30737                let distinct = self.match_token(TokenType::Distinct);
30738                let this = self.parse_expression()?;
30739                // ClickHouse: handle AS alias inside countIf args: countIf(expr AS d, pred)
30740                let this = if matches!(
30741                    self.config.dialect,
30742                    Some(crate::dialects::DialectType::ClickHouse)
30743                ) && self.check(TokenType::As)
30744                {
30745                    let next_idx = self.current + 1;
30746                    let after_alias_idx = self.current + 2;
30747                    let is_alias = next_idx < self.tokens.len()
30748                        && (matches!(
30749                            self.tokens[next_idx].token_type,
30750                            TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
30751                        ) || self.tokens[next_idx].token_type.is_keyword())
30752                        && after_alias_idx < self.tokens.len()
30753                        && matches!(
30754                            self.tokens[after_alias_idx].token_type,
30755                            TokenType::RParen | TokenType::Comma
30756                        );
30757                    if is_alias {
30758                        self.skip(); // consume AS
30759                        let alias_token = self.advance();
30760                        Expression::Alias(Box::new(crate::expressions::Alias {
30761                            this,
30762                            alias: Identifier::new(alias_token.text.clone()),
30763                            column_aliases: Vec::new(),
30764                            pre_alias_comments: Vec::new(),
30765                            trailing_comments: Vec::new(),
30766                            inferred_type: None,
30767                        }))
30768                    } else {
30769                        this
30770                    }
30771                } else {
30772                    this
30773                };
30774                if matches!(
30775                    self.config.dialect,
30776                    Some(crate::dialects::DialectType::ClickHouse)
30777                ) && self.match_token(TokenType::Comma)
30778                {
30779                    let mut args = vec![this];
30780                    let arg = self.parse_expression()?;
30781                    // Handle AS alias on subsequent args too
30782                    let arg = if self.check(TokenType::As) {
30783                        let next_idx = self.current + 1;
30784                        let after_alias_idx = self.current + 2;
30785                        let is_alias = next_idx < self.tokens.len()
30786                            && (matches!(
30787                                self.tokens[next_idx].token_type,
30788                                TokenType::Identifier
30789                                    | TokenType::Var
30790                                    | TokenType::QuotedIdentifier
30791                            ) || self.tokens[next_idx].token_type.is_keyword())
30792                            && after_alias_idx < self.tokens.len()
30793                            && matches!(
30794                                self.tokens[after_alias_idx].token_type,
30795                                TokenType::RParen | TokenType::Comma
30796                            );
30797                        if is_alias {
30798                            self.skip(); // consume AS
30799                            let alias_token = self.advance();
30800                            Expression::Alias(Box::new(crate::expressions::Alias {
30801                                this: arg,
30802                                alias: Identifier::new(alias_token.text.clone()),
30803                                column_aliases: Vec::new(),
30804                                pre_alias_comments: Vec::new(),
30805                                trailing_comments: Vec::new(),
30806                                inferred_type: None,
30807                            }))
30808                        } else {
30809                            arg
30810                        }
30811                    } else {
30812                        arg
30813                    };
30814                    args.push(arg);
30815                    while self.match_token(TokenType::Comma) {
30816                        args.push(self.parse_expression()?);
30817                    }
30818                    self.expect(TokenType::RParen)?;
30819                    return Ok(Some(Expression::CombinedAggFunc(Box::new(
30820                        CombinedAggFunc {
30821                            this: Box::new(Expression::Identifier(Identifier::new("countIf"))),
30822                            expressions: args,
30823                        },
30824                    ))));
30825                }
30826                self.expect(TokenType::RParen)?;
30827                let filter = self.parse_filter_clause()?;
30828                Ok(Some(Expression::CountIf(Box::new(AggFunc {
30829                    ignore_nulls: None,
30830                    this,
30831                    distinct,
30832                    filter,
30833                    order_by: Vec::new(),
30834                    having_max: None,
30835                    name: Some(name.to_string()),
30836                    limit: None,
30837                    inferred_type: None,
30838                }))))
30839            }
30840            (crate::function_registry::TypedParseKind::Binary, "STARTS_WITH")
30841            | (crate::function_registry::TypedParseKind::Binary, "ENDS_WITH") => {
30842                let this = self.parse_expression()?;
30843                self.expect(TokenType::Comma)?;
30844                let expression = self.parse_expression()?;
30845                self.expect(TokenType::RParen)?;
30846                let func = BinaryFunc {
30847                    original_name: None,
30848                    this,
30849                    expression,
30850                    inferred_type: None,
30851                };
30852                let expr = match spec.canonical_name {
30853                    "STARTS_WITH" => Expression::StartsWith(Box::new(func)),
30854                    "ENDS_WITH" => Expression::EndsWith(Box::new(func)),
30855                    _ => unreachable!("binary typed parse kind already matched in caller"),
30856                };
30857                Ok(Some(expr))
30858            }
30859            (crate::function_registry::TypedParseKind::Binary, "ATAN2") => {
30860                let this = self.parse_expression()?;
30861                self.expect(TokenType::Comma)?;
30862                let expression = self.parse_expression()?;
30863                self.expect(TokenType::RParen)?;
30864                Ok(Some(Expression::Atan2(Box::new(BinaryFunc {
30865                    original_name: None,
30866                    this,
30867                    expression,
30868                    inferred_type: None,
30869                }))))
30870            }
30871            (crate::function_registry::TypedParseKind::Binary, "MAP_FROM_ARRAYS")
30872            | (crate::function_registry::TypedParseKind::Binary, "MAP_CONTAINS_KEY")
30873            | (crate::function_registry::TypedParseKind::Binary, "ELEMENT_AT") => {
30874                let this = self.parse_expression()?;
30875                self.expect(TokenType::Comma)?;
30876                let expression = self.parse_expression()?;
30877                self.expect(TokenType::RParen)?;
30878                let func = BinaryFunc {
30879                    original_name: None,
30880                    this,
30881                    expression,
30882                    inferred_type: None,
30883                };
30884                let expr = match spec.canonical_name {
30885                    "MAP_FROM_ARRAYS" => Expression::MapFromArrays(Box::new(func)),
30886                    "MAP_CONTAINS_KEY" => Expression::MapContainsKey(Box::new(func)),
30887                    "ELEMENT_AT" => Expression::ElementAt(Box::new(func)),
30888                    _ => unreachable!("binary map parse kind already matched in caller"),
30889                };
30890                Ok(Some(expr))
30891            }
30892            (crate::function_registry::TypedParseKind::Binary, "CONTAINS")
30893            | (crate::function_registry::TypedParseKind::Binary, "MOD")
30894            | (crate::function_registry::TypedParseKind::Binary, "POW") => {
30895                let this = self.parse_expression()?;
30896                self.expect(TokenType::Comma)?;
30897                let expression = self.parse_expression()?;
30898                self.expect(TokenType::RParen)?;
30899                let expr = match spec.canonical_name {
30900                    "CONTAINS" => Expression::Contains(Box::new(BinaryFunc {
30901                        original_name: None,
30902                        this,
30903                        expression,
30904                        inferred_type: None,
30905                    })),
30906                    "MOD" => Expression::ModFunc(Box::new(BinaryFunc {
30907                        original_name: None,
30908                        this,
30909                        expression,
30910                        inferred_type: None,
30911                    })),
30912                    "POW" => Expression::Power(Box::new(BinaryFunc {
30913                        original_name: None,
30914                        this,
30915                        expression,
30916                        inferred_type: None,
30917                    })),
30918                    _ => unreachable!("binary scalar parse kind already matched in caller"),
30919                };
30920                Ok(Some(expr))
30921            }
30922            (crate::function_registry::TypedParseKind::Binary, "ADD_MONTHS")
30923            | (crate::function_registry::TypedParseKind::Binary, "MONTHS_BETWEEN")
30924            | (crate::function_registry::TypedParseKind::Binary, "NEXT_DAY") => {
30925                let this = self.parse_expression()?;
30926                self.expect(TokenType::Comma)?;
30927                let expression = self.parse_expression()?;
30928                if spec.canonical_name == "MONTHS_BETWEEN" && self.match_token(TokenType::Comma) {
30929                    let round_off = self.parse_expression()?;
30930                    self.expect(TokenType::RParen)?;
30931                    return Ok(Some(Expression::Function(Box::new(
30932                        crate::expressions::Function::new(
30933                            "MONTHS_BETWEEN".to_string(),
30934                            vec![this, expression, round_off],
30935                        ),
30936                    ))));
30937                }
30938                self.expect(TokenType::RParen)?;
30939                let func = BinaryFunc {
30940                    original_name: None,
30941                    this,
30942                    expression,
30943                    inferred_type: None,
30944                };
30945                let expr = match spec.canonical_name {
30946                    "ADD_MONTHS" => Expression::AddMonths(Box::new(func)),
30947                    "MONTHS_BETWEEN" => Expression::MonthsBetween(Box::new(func)),
30948                    "NEXT_DAY" => Expression::NextDay(Box::new(func)),
30949                    _ => unreachable!("date binary parse kind already matched in caller"),
30950                };
30951                Ok(Some(expr))
30952            }
30953            (crate::function_registry::TypedParseKind::Binary, "ARRAY_CONTAINS")
30954            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_POSITION")
30955            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_APPEND")
30956            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_PREPEND")
30957            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_UNION")
30958            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_EXCEPT")
30959            | (crate::function_registry::TypedParseKind::Binary, "ARRAY_REMOVE") => {
30960                let this = self.parse_expression()?;
30961                self.expect(TokenType::Comma)?;
30962                let expression = self.parse_expression()?;
30963                self.expect(TokenType::RParen)?;
30964                let func = BinaryFunc {
30965                    original_name: None,
30966                    this,
30967                    expression,
30968                    inferred_type: None,
30969                };
30970                let expr = match spec.canonical_name {
30971                    "ARRAY_CONTAINS" => Expression::ArrayContains(Box::new(func)),
30972                    "ARRAY_POSITION" => Expression::ArrayPosition(Box::new(func)),
30973                    "ARRAY_APPEND" => Expression::ArrayAppend(Box::new(func)),
30974                    "ARRAY_PREPEND" => Expression::ArrayPrepend(Box::new(func)),
30975                    "ARRAY_UNION" => Expression::ArrayUnion(Box::new(func)),
30976                    "ARRAY_EXCEPT" => Expression::ArrayExcept(Box::new(func)),
30977                    "ARRAY_REMOVE" => Expression::ArrayRemove(Box::new(func)),
30978                    _ => unreachable!("array binary parse kind already matched in caller"),
30979                };
30980                Ok(Some(expr))
30981            }
30982            (crate::function_registry::TypedParseKind::Unary, "LENGTH") => {
30983                let this = self.parse_expression()?;
30984                // PostgreSQL: LENGTH(string, encoding) accepts optional second argument
30985                if self.match_token(TokenType::Comma) {
30986                    let encoding = self.parse_expression()?;
30987                    self.expect(TokenType::RParen)?;
30988                    // Store as a regular function to preserve both arguments
30989                    Ok(Some(Expression::Function(Box::new(Function::new(
30990                        upper_name,
30991                        vec![this, encoding],
30992                    )))))
30993                } else {
30994                    self.expect(TokenType::RParen)?;
30995                    Ok(Some(Expression::Length(Box::new(UnaryFunc::new(this)))))
30996                }
30997            }
30998            (crate::function_registry::TypedParseKind::Unary, "LOWER") => {
30999                let this = self.parse_expression_with_clickhouse_alias()?;
31000                self.expect(TokenType::RParen)?;
31001                Ok(Some(Expression::Lower(Box::new(UnaryFunc::new(this)))))
31002            }
31003            (crate::function_registry::TypedParseKind::Unary, "UPPER") => {
31004                let this = self.parse_expression_with_clickhouse_alias()?;
31005                self.expect(TokenType::RParen)?;
31006                Ok(Some(Expression::Upper(Box::new(UnaryFunc::new(this)))))
31007            }
31008            (crate::function_registry::TypedParseKind::Unary, "TYPEOF") => {
31009                let this = self.parse_expression()?;
31010                // ClickHouse: expr AS alias inside function args
31011                let this = self.maybe_clickhouse_alias(this);
31012                if self.match_token(TokenType::Comma) {
31013                    // Preserve additional args via generic function form
31014                    let mut all_args = vec![this];
31015                    let remaining = self.parse_function_arguments()?;
31016                    all_args.extend(remaining);
31017                    self.expect(TokenType::RParen)?;
31018                    Ok(Some(Expression::Function(Box::new(Function {
31019                        name: name.to_string(),
31020                        args: all_args,
31021                        distinct: false,
31022                        trailing_comments: Vec::new(),
31023                        use_bracket_syntax: false,
31024                        no_parens: false,
31025                        quoted: false,
31026                        span: None,
31027                        inferred_type: None,
31028                    }))))
31029                } else {
31030                    self.expect(TokenType::RParen)?;
31031                    Ok(Some(Expression::Typeof(Box::new(UnaryFunc::new(this)))))
31032                }
31033            }
31034            (crate::function_registry::TypedParseKind::Unary, "DAYOFWEEK")
31035            | (crate::function_registry::TypedParseKind::Unary, "DAYOFYEAR")
31036            | (crate::function_registry::TypedParseKind::Unary, "DAYOFMONTH")
31037            | (crate::function_registry::TypedParseKind::Unary, "WEEKOFYEAR") => {
31038                let this = self.parse_expression()?;
31039                self.expect(TokenType::RParen)?;
31040                let func = UnaryFunc::new(this);
31041                let expr = match spec.canonical_name {
31042                    "DAYOFWEEK" => Expression::DayOfWeek(Box::new(func)),
31043                    "DAYOFYEAR" => Expression::DayOfYear(Box::new(func)),
31044                    "DAYOFMONTH" => Expression::DayOfMonth(Box::new(func)),
31045                    "WEEKOFYEAR" => Expression::WeekOfYear(Box::new(func)),
31046                    _ => unreachable!("date-part unary parse kind already matched in caller"),
31047                };
31048                Ok(Some(expr))
31049            }
31050            (crate::function_registry::TypedParseKind::Unary, "SIN")
31051            | (crate::function_registry::TypedParseKind::Unary, "COS")
31052            | (crate::function_registry::TypedParseKind::Unary, "TAN")
31053            | (crate::function_registry::TypedParseKind::Unary, "ASIN")
31054            | (crate::function_registry::TypedParseKind::Unary, "ACOS")
31055            | (crate::function_registry::TypedParseKind::Unary, "ATAN")
31056            | (crate::function_registry::TypedParseKind::Unary, "RADIANS")
31057            | (crate::function_registry::TypedParseKind::Unary, "DEGREES") => {
31058                let this = self.parse_expression()?;
31059                // MySQL: ATAN(y, x) with 2 args is equivalent to ATAN2(y, x)
31060                if spec.canonical_name == "ATAN" && self.match_token(TokenType::Comma) {
31061                    let expression = self.parse_expression()?;
31062                    self.expect(TokenType::RParen)?;
31063                    return Ok(Some(Expression::Atan2(Box::new(BinaryFunc {
31064                        original_name: Some("ATAN".to_string()),
31065                        this,
31066                        expression,
31067                        inferred_type: None,
31068                    }))));
31069                }
31070                self.expect(TokenType::RParen)?;
31071                let func = UnaryFunc::new(this);
31072                let expr = match spec.canonical_name {
31073                    "SIN" => Expression::Sin(Box::new(func)),
31074                    "COS" => Expression::Cos(Box::new(func)),
31075                    "TAN" => Expression::Tan(Box::new(func)),
31076                    "ASIN" => Expression::Asin(Box::new(func)),
31077                    "ACOS" => Expression::Acos(Box::new(func)),
31078                    "ATAN" => Expression::Atan(Box::new(func)),
31079                    "RADIANS" => Expression::Radians(Box::new(func)),
31080                    "DEGREES" => Expression::Degrees(Box::new(func)),
31081                    _ => unreachable!("trig unary parse kind already matched in caller"),
31082                };
31083                Ok(Some(expr))
31084            }
31085            (crate::function_registry::TypedParseKind::Unary, "YEAR")
31086            | (crate::function_registry::TypedParseKind::Unary, "MONTH")
31087            | (crate::function_registry::TypedParseKind::Unary, "DAY")
31088            | (crate::function_registry::TypedParseKind::Unary, "HOUR")
31089            | (crate::function_registry::TypedParseKind::Unary, "MINUTE")
31090            | (crate::function_registry::TypedParseKind::Unary, "SECOND")
31091            | (crate::function_registry::TypedParseKind::Unary, "DAYOFWEEK_ISO")
31092            | (crate::function_registry::TypedParseKind::Unary, "QUARTER")
31093            | (crate::function_registry::TypedParseKind::Unary, "EPOCH")
31094            | (crate::function_registry::TypedParseKind::Unary, "EPOCH_MS") => {
31095                let this = self.parse_expression()?;
31096                self.expect(TokenType::RParen)?;
31097                let func = UnaryFunc::new(this);
31098                let expr = match spec.canonical_name {
31099                    "YEAR" => Expression::Year(Box::new(func)),
31100                    "MONTH" => Expression::Month(Box::new(func)),
31101                    "DAY" => Expression::Day(Box::new(func)),
31102                    "HOUR" => Expression::Hour(Box::new(func)),
31103                    "MINUTE" => Expression::Minute(Box::new(func)),
31104                    "SECOND" => Expression::Second(Box::new(func)),
31105                    "DAYOFWEEK_ISO" => Expression::DayOfWeekIso(Box::new(func)),
31106                    "QUARTER" => Expression::Quarter(Box::new(func)),
31107                    "EPOCH" => Expression::Epoch(Box::new(func)),
31108                    "EPOCH_MS" => Expression::EpochMs(Box::new(func)),
31109                    _ => unreachable!("date unary parse kind already matched in caller"),
31110                };
31111                Ok(Some(expr))
31112            }
31113            (crate::function_registry::TypedParseKind::Unary, "ARRAY_LENGTH")
31114            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_SIZE")
31115            | (crate::function_registry::TypedParseKind::Unary, "CARDINALITY")
31116            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_REVERSE")
31117            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_DISTINCT")
31118            | (crate::function_registry::TypedParseKind::Unary, "ARRAY_COMPACT")
31119            | (crate::function_registry::TypedParseKind::Unary, "EXPLODE")
31120            | (crate::function_registry::TypedParseKind::Unary, "EXPLODE_OUTER") => {
31121                let this = self.parse_expression()?;
31122                // PostgreSQL ARRAY_LENGTH and ARRAY_SIZE can take a second dimension arg.
31123                // Preserve that by falling back to generic function form for 2-arg usage.
31124                if (spec.canonical_name == "ARRAY_LENGTH" || spec.canonical_name == "ARRAY_SIZE")
31125                    && self.match_token(TokenType::Comma)
31126                {
31127                    let dimension = self.parse_expression()?;
31128                    self.expect(TokenType::RParen)?;
31129                    return Ok(Some(Expression::Function(Box::new(Function {
31130                        name: name.to_string(),
31131                        args: vec![this, dimension],
31132                        distinct: false,
31133                        trailing_comments: Vec::new(),
31134                        use_bracket_syntax: false,
31135                        no_parens: false,
31136                        quoted: false,
31137                        span: None,
31138                        inferred_type: None,
31139                    }))));
31140                }
31141                self.expect(TokenType::RParen)?;
31142                let func = UnaryFunc::new(this);
31143                let expr = match spec.canonical_name {
31144                    "ARRAY_LENGTH" => Expression::ArrayLength(Box::new(func)),
31145                    "ARRAY_SIZE" => Expression::ArraySize(Box::new(func)),
31146                    "CARDINALITY" => Expression::Cardinality(Box::new(func)),
31147                    "ARRAY_REVERSE" => Expression::ArrayReverse(Box::new(func)),
31148                    "ARRAY_DISTINCT" => Expression::ArrayDistinct(Box::new(func)),
31149                    "ARRAY_COMPACT" => Expression::ArrayCompact(Box::new(func)),
31150                    "EXPLODE" => Expression::Explode(Box::new(func)),
31151                    "EXPLODE_OUTER" => Expression::ExplodeOuter(Box::new(func)),
31152                    _ => unreachable!("array unary parse kind already matched in caller"),
31153                };
31154                Ok(Some(expr))
31155            }
31156            (crate::function_registry::TypedParseKind::Unary, "MAP_FROM_ENTRIES")
31157            | (crate::function_registry::TypedParseKind::Unary, "MAP_KEYS")
31158            | (crate::function_registry::TypedParseKind::Unary, "MAP_VALUES") => {
31159                let this = self.parse_expression()?;
31160                self.expect(TokenType::RParen)?;
31161                let func = UnaryFunc::new(this);
31162                let expr = match spec.canonical_name {
31163                    "MAP_FROM_ENTRIES" => Expression::MapFromEntries(Box::new(func)),
31164                    "MAP_KEYS" => Expression::MapKeys(Box::new(func)),
31165                    "MAP_VALUES" => Expression::MapValues(Box::new(func)),
31166                    _ => unreachable!("map unary parse kind already matched in caller"),
31167                };
31168                Ok(Some(expr))
31169            }
31170            (crate::function_registry::TypedParseKind::Unary, "ABS") => {
31171                let this = self.parse_expression_with_clickhouse_alias()?;
31172                self.expect(TokenType::RParen)?;
31173                Ok(Some(Expression::Abs(Box::new(UnaryFunc::new(this)))))
31174            }
31175            (crate::function_registry::TypedParseKind::Unary, "SQRT")
31176            | (crate::function_registry::TypedParseKind::Unary, "EXP")
31177            | (crate::function_registry::TypedParseKind::Unary, "LN") => {
31178                let this = self.parse_expression()?;
31179                self.expect(TokenType::RParen)?;
31180                let expr = match spec.canonical_name {
31181                    "SQRT" => Expression::Sqrt(Box::new(UnaryFunc::new(this))),
31182                    "EXP" => Expression::Exp(Box::new(UnaryFunc::new(this))),
31183                    "LN" => Expression::Ln(Box::new(UnaryFunc::new(this))),
31184                    _ => unreachable!("math unary parse kind already matched in caller"),
31185                };
31186                Ok(Some(expr))
31187            }
31188            (crate::function_registry::TypedParseKind::Variadic, "TO_NUMBER")
31189            | (crate::function_registry::TypedParseKind::Variadic, "TRY_TO_NUMBER") => {
31190                let args = self.parse_expression_list()?;
31191                self.expect(TokenType::RParen)?;
31192                let this = args.get(0).cloned().unwrap_or(Expression::Null(Null {}));
31193                let format = args.get(1).cloned().map(Box::new);
31194                let precision = args.get(2).cloned().map(Box::new);
31195                let scale = args.get(3).cloned().map(Box::new);
31196                let safe = if spec.canonical_name == "TRY_TO_NUMBER" {
31197                    Some(Box::new(Expression::Boolean(BooleanLiteral {
31198                        value: true,
31199                    })))
31200                } else {
31201                    None
31202                };
31203                Ok(Some(Expression::ToNumber(Box::new(ToNumber {
31204                    this: Box::new(this),
31205                    format,
31206                    nlsparam: None,
31207                    precision,
31208                    scale,
31209                    safe,
31210                    safe_name: None,
31211                }))))
31212            }
31213            (crate::function_registry::TypedParseKind::Variadic, "SUBSTRING") => {
31214                let this = self.parse_expression()?;
31215                // ClickHouse: implicit/explicit alias: substring('1234' lhs FROM 2) or substring('1234' AS lhs FROM 2)
31216                let this = self.try_clickhouse_func_arg_alias(this);
31217
31218                // Check for SQL standard FROM syntax: SUBSTRING(str FROM pos [FOR len])
31219                if self.match_token(TokenType::From) {
31220                    let start = self.parse_expression()?;
31221                    let start = self.try_clickhouse_func_arg_alias(start);
31222                    let length = if self.match_token(TokenType::For) {
31223                        let len = self.parse_expression()?;
31224                        Some(self.try_clickhouse_func_arg_alias(len))
31225                    } else {
31226                        None
31227                    };
31228                    self.expect(TokenType::RParen)?;
31229                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
31230                        this,
31231                        start,
31232                        length,
31233                        from_for_syntax: true,
31234                    }))))
31235                } else if self.match_token(TokenType::For) {
31236                    // PostgreSQL: SUBSTRING(str FOR len) or SUBSTRING(str FOR len FROM pos)
31237                    let length_expr = self.parse_expression()?;
31238                    let length_expr = self.try_clickhouse_func_arg_alias(length_expr);
31239                    let start = if self.match_token(TokenType::From) {
31240                        let s = self.parse_expression()?;
31241                        self.try_clickhouse_func_arg_alias(s)
31242                    } else {
31243                        // No FROM, use 1 as default start position
31244                        Expression::Literal(Box::new(Literal::Number("1".to_string())))
31245                    };
31246                    self.expect(TokenType::RParen)?;
31247                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
31248                        this,
31249                        start,
31250                        length: Some(length_expr),
31251                        from_for_syntax: true,
31252                    }))))
31253                } else if self.match_token(TokenType::Comma) {
31254                    // Comma-separated syntax: SUBSTRING(str, pos) or SUBSTRING(str, pos, len)
31255                    let start = self.parse_expression()?;
31256                    let start = self.try_clickhouse_func_arg_alias(start);
31257                    let length = if self.match_token(TokenType::Comma) {
31258                        let len = self.parse_expression()?;
31259                        Some(self.try_clickhouse_func_arg_alias(len))
31260                    } else {
31261                        None
31262                    };
31263                    self.expect(TokenType::RParen)?;
31264                    Ok(Some(Expression::Substring(Box::new(SubstringFunc {
31265                        this,
31266                        start,
31267                        length,
31268                        from_for_syntax: false,
31269                    }))))
31270                } else {
31271                    // Just SUBSTRING(str) with no other args - unusual but handle it
31272                    self.expect(TokenType::RParen)?;
31273                    // Treat as function call
31274                    Ok(Some(Expression::Function(Box::new(Function {
31275                        name: name.to_string(),
31276                        args: vec![this],
31277                        distinct: false,
31278                        trailing_comments: Vec::new(),
31279                        use_bracket_syntax: false,
31280                        no_parens: false,
31281                        quoted: false,
31282                        span: None,
31283                        inferred_type: None,
31284                    }))))
31285                }
31286            }
31287            (crate::function_registry::TypedParseKind::Variadic, "DATE_PART") => {
31288                let part = self.parse_expression()?;
31289                // For TSQL/Fabric, normalize date part aliases (e.g., "dd" -> DAY)
31290                let mut part = if matches!(
31291                    self.config.dialect,
31292                    Some(crate::dialects::DialectType::TSQL)
31293                        | Some(crate::dialects::DialectType::Fabric)
31294                ) {
31295                    self.normalize_tsql_date_part(part)
31296                } else {
31297                    part
31298                };
31299                // Accept both FROM and comma as separator (Snowflake supports both syntaxes)
31300                if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
31301                    return Err(self.parse_error("Expected FROM or comma in DATE_PART"));
31302                }
31303                let from_expr = self.parse_expression()?;
31304                self.expect(TokenType::RParen)?;
31305                if matches!(
31306                    self.config.dialect,
31307                    Some(crate::dialects::DialectType::Snowflake)
31308                ) {
31309                    if self
31310                        .try_parse_date_part_field_identifier_expr(&part)
31311                        .is_some()
31312                    {
31313                        part = self.convert_date_part_identifier_expr_to_var(part);
31314                    }
31315                }
31316                Ok(Some(Expression::Function(Box::new(Function {
31317                    name: "DATE_PART".to_string(),
31318                    args: vec![part, from_expr],
31319                    distinct: false,
31320                    trailing_comments: Vec::new(),
31321                    use_bracket_syntax: false,
31322                    no_parens: false,
31323                    quoted: false,
31324                    span: None,
31325                    inferred_type: None,
31326                }))))
31327            }
31328            (crate::function_registry::TypedParseKind::Variadic, "DATEADD") => {
31329                let mut first_arg = self.parse_expression()?;
31330                first_arg = self.try_clickhouse_func_arg_alias(first_arg);
31331                self.expect(TokenType::Comma)?;
31332                let second_arg = self.parse_expression()?;
31333                let second_arg = self.try_clickhouse_func_arg_alias(second_arg);
31334
31335                // Check if there's a third argument (traditional 3-arg syntax)
31336                if self.match_token(TokenType::Comma) {
31337                    let third_arg = self.parse_expression()?;
31338                    let third_arg = self.try_clickhouse_func_arg_alias(third_arg);
31339                    self.expect(TokenType::RParen)?;
31340                    if matches!(
31341                        self.config.dialect,
31342                        Some(crate::dialects::DialectType::Snowflake)
31343                    ) {
31344                        if self
31345                            .try_parse_date_part_unit_identifier_expr(&first_arg)
31346                            .is_some()
31347                        {
31348                            first_arg = self.convert_date_part_identifier_expr_to_var(first_arg);
31349                        }
31350                    }
31351                    Ok(Some(Expression::Function(Box::new(Function {
31352                        name: name.to_string(),
31353                        args: vec![first_arg, second_arg, third_arg],
31354                        distinct: false,
31355                        trailing_comments: Vec::new(),
31356                        use_bracket_syntax: false,
31357                        no_parens: false,
31358                        quoted: false,
31359                        span: None,
31360                        inferred_type: None,
31361                    }))))
31362                } else {
31363                    // BigQuery 2-arg syntax: DATE_ADD(date, interval)
31364                    self.expect(TokenType::RParen)?;
31365                    Ok(Some(Expression::Function(Box::new(Function {
31366                        name: name.to_string(),
31367                        args: vec![first_arg, second_arg],
31368                        distinct: false,
31369                        trailing_comments: Vec::new(),
31370                        use_bracket_syntax: false,
31371                        no_parens: false,
31372                        quoted: false,
31373                        span: None,
31374                        inferred_type: None,
31375                    }))))
31376                }
31377            }
31378            (crate::function_registry::TypedParseKind::Variadic, "DATEDIFF") => {
31379                // First argument (can be unit for DATEDIFF/TIMESTAMPDIFF or datetime for TIMEDIFF)
31380                let first_arg = self.parse_expression()?;
31381                let first_arg = self.try_clickhouse_func_arg_alias(first_arg);
31382                self.expect(TokenType::Comma)?;
31383                let second_arg = self.parse_expression()?;
31384                let second_arg = self.try_clickhouse_func_arg_alias(second_arg);
31385                // Third argument is optional (SQLite TIMEDIFF only takes 2 args)
31386                let mut args = if self.match_token(TokenType::Comma) {
31387                    let third_arg = self.parse_expression()?;
31388                    let third_arg = self.try_clickhouse_func_arg_alias(third_arg);
31389                    vec![first_arg, second_arg, third_arg]
31390                } else {
31391                    vec![first_arg, second_arg]
31392                };
31393                // ClickHouse: optional 4th timezone argument for dateDiff
31394                while self.match_token(TokenType::Comma) {
31395                    let arg = self.parse_expression()?;
31396                    args.push(self.try_clickhouse_func_arg_alias(arg));
31397                }
31398                self.expect(TokenType::RParen)?;
31399                if matches!(
31400                    self.config.dialect,
31401                    Some(crate::dialects::DialectType::Snowflake)
31402                ) && args.len() == 3
31403                {
31404                    if let Some(unit) = self.try_parse_date_part_unit_expr(&args[0]) {
31405                        return Ok(Some(Expression::DateDiff(Box::new(DateDiffFunc {
31406                            this: args[2].clone(),
31407                            expression: args[1].clone(),
31408                            unit: Some(unit),
31409                        }))));
31410                    }
31411                }
31412                Ok(Some(Expression::Function(Box::new(Function {
31413                    name: name.to_string(),
31414                    args,
31415                    distinct: false,
31416                    trailing_comments: Vec::new(),
31417                    use_bracket_syntax: false,
31418                    no_parens: false,
31419                    quoted: false,
31420                    span: None,
31421                    inferred_type: None,
31422                }))))
31423            }
31424            (crate::function_registry::TypedParseKind::Variadic, "RANDOM") => {
31425                // RANDOM() - no args, RANDOM(seed) - Snowflake, RANDOM(lower, upper) - Teradata
31426                if self.check(TokenType::RParen) {
31427                    self.expect(TokenType::RParen)?;
31428                    Ok(Some(Expression::Random(Random)))
31429                } else {
31430                    let first = self.parse_expression()?;
31431                    if self.match_token(TokenType::Comma) {
31432                        let second = self.parse_expression()?;
31433                        self.expect(TokenType::RParen)?;
31434                        Ok(Some(Expression::Rand(Box::new(Rand {
31435                            seed: None,
31436                            lower: Some(Box::new(first)),
31437                            upper: Some(Box::new(second)),
31438                        }))))
31439                    } else {
31440                        self.expect(TokenType::RParen)?;
31441                        Ok(Some(Expression::Rand(Box::new(Rand {
31442                            seed: Some(Box::new(first)),
31443                            lower: None,
31444                            upper: None,
31445                        }))))
31446                    }
31447                }
31448            }
31449            (crate::function_registry::TypedParseKind::Variadic, "RAND") => {
31450                let seed = if self.check(TokenType::RParen) {
31451                    None
31452                } else {
31453                    Some(Box::new(self.parse_expression()?))
31454                };
31455                self.expect(TokenType::RParen)?;
31456                Ok(Some(Expression::Rand(Box::new(Rand {
31457                    seed,
31458                    lower: None,
31459                    upper: None,
31460                }))))
31461            }
31462            (crate::function_registry::TypedParseKind::Variadic, "PI") => {
31463                self.expect(TokenType::RParen)?;
31464                Ok(Some(Expression::Pi(Pi)))
31465            }
31466            (crate::function_registry::TypedParseKind::Variadic, "LAST_DAY") => {
31467                let this = self.parse_expression()?;
31468                let unit = if self.match_token(TokenType::Comma) {
31469                    Some(self.parse_datetime_field()?)
31470                } else {
31471                    None
31472                };
31473                self.expect(TokenType::RParen)?;
31474                Ok(Some(Expression::LastDay(Box::new(LastDayFunc {
31475                    this,
31476                    unit,
31477                }))))
31478            }
31479            (crate::function_registry::TypedParseKind::Variadic, "POSITION") => {
31480                let expr = self
31481                    .parse_position()?
31482                    .ok_or_else(|| self.parse_error("Expected expression in POSITION"))?;
31483                self.expect(TokenType::RParen)?;
31484                Ok(Some(expr))
31485            }
31486            (crate::function_registry::TypedParseKind::Variadic, "STRPOS") => {
31487                let this = self.parse_expression()?;
31488                self.expect(TokenType::Comma)?;
31489                let substr = self.parse_expression()?;
31490                let occurrence = if self.match_token(TokenType::Comma) {
31491                    Some(Box::new(self.parse_expression()?))
31492                } else {
31493                    None
31494                };
31495                self.expect(TokenType::RParen)?;
31496                Ok(Some(Expression::StrPosition(Box::new(StrPosition {
31497                    this: Box::new(this),
31498                    substr: Some(Box::new(substr)),
31499                    position: None,
31500                    occurrence,
31501                }))))
31502            }
31503            (crate::function_registry::TypedParseKind::Variadic, "LOCATE") => {
31504                if self.check(TokenType::RParen) {
31505                    self.skip();
31506                    return Ok(Some(Expression::Function(Box::new(Function {
31507                        name: name.to_string(),
31508                        args: vec![],
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                let first = self.parse_expression()?;
31519                if !self.check(TokenType::Comma) && self.check(TokenType::RParen) {
31520                    self.skip();
31521                    return Ok(Some(Expression::Function(Box::new(Function {
31522                        name: name.to_string(),
31523                        args: vec![first],
31524                        distinct: false,
31525                        trailing_comments: Vec::new(),
31526                        use_bracket_syntax: false,
31527                        no_parens: false,
31528                        quoted: false,
31529                        span: None,
31530                        inferred_type: None,
31531                    }))));
31532                }
31533                self.expect(TokenType::Comma)?;
31534                let second = self.parse_expression()?;
31535                let position = if self.match_token(TokenType::Comma) {
31536                    Some(Box::new(self.parse_expression()?))
31537                } else {
31538                    None
31539                };
31540                self.expect(TokenType::RParen)?;
31541                Ok(Some(Expression::StrPosition(Box::new(StrPosition {
31542                    this: Box::new(second),
31543                    substr: Some(Box::new(first)),
31544                    position,
31545                    occurrence: None,
31546                }))))
31547            }
31548            (crate::function_registry::TypedParseKind::Variadic, "INSTR") => {
31549                let first = self.parse_expression()?;
31550                self.expect(TokenType::Comma)?;
31551                let second = self.parse_expression()?;
31552                let position = 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::StrPosition(Box::new(StrPosition {
31559                    this: Box::new(first),
31560                    substr: Some(Box::new(second)),
31561                    position,
31562                    occurrence: None,
31563                }))))
31564            }
31565            (crate::function_registry::TypedParseKind::Variadic, "NORMALIZE") => {
31566                let this = self.parse_expression()?;
31567                let form = if self.match_token(TokenType::Comma) {
31568                    Some(Box::new(self.parse_expression()?))
31569                } else {
31570                    None
31571                };
31572                self.expect(TokenType::RParen)?;
31573                Ok(Some(Expression::Normalize(Box::new(Normalize {
31574                    this: Box::new(this),
31575                    form,
31576                    is_casefold: None,
31577                }))))
31578            }
31579            (crate::function_registry::TypedParseKind::Variadic, "INITCAP") => {
31580                let this = self.parse_expression()?;
31581                let delimiter = if self.match_token(TokenType::Comma) {
31582                    Some(Box::new(self.parse_expression()?))
31583                } else {
31584                    None
31585                };
31586                self.expect(TokenType::RParen)?;
31587                if let Some(delim) = delimiter {
31588                    Ok(Some(Expression::Function(Box::new(Function::new(
31589                        "INITCAP".to_string(),
31590                        vec![this, *delim],
31591                    )))))
31592                } else {
31593                    Ok(Some(Expression::Initcap(Box::new(UnaryFunc::new(this)))))
31594                }
31595            }
31596            (crate::function_registry::TypedParseKind::Variadic, "FLOOR") => {
31597                let this = self.parse_expression()?;
31598                let to = if self.match_token(TokenType::To) {
31599                    self.parse_var()?
31600                } else {
31601                    None
31602                };
31603                let scale = if to.is_none() && self.match_token(TokenType::Comma) {
31604                    Some(self.parse_expression()?)
31605                } else {
31606                    None
31607                };
31608                if self.check(TokenType::Comma) {
31609                    let mut args = vec![this];
31610                    if let Some(s) = scale {
31611                        args.push(s);
31612                    }
31613                    while self.match_token(TokenType::Comma) {
31614                        args.push(self.parse_expression()?);
31615                    }
31616                    self.expect(TokenType::RParen)?;
31617                    return Ok(Some(Expression::Function(Box::new(Function {
31618                        name: name.to_string(),
31619                        args,
31620                        distinct: false,
31621                        trailing_comments: Vec::new(),
31622                        use_bracket_syntax: false,
31623                        no_parens: false,
31624                        quoted: false,
31625                        span: None,
31626                        inferred_type: None,
31627                    }))));
31628                }
31629                self.expect(TokenType::RParen)?;
31630                Ok(Some(Expression::Floor(Box::new(FloorFunc {
31631                    this,
31632                    scale,
31633                    to,
31634                }))))
31635            }
31636            (crate::function_registry::TypedParseKind::Variadic, "LOG") => {
31637                let first = self.parse_expression()?;
31638                if self.match_token(TokenType::Comma) {
31639                    let second = self.parse_expression()?;
31640                    self.expect(TokenType::RParen)?;
31641                    let (value, base) = if self.log_base_first() {
31642                        (second, first)
31643                    } else {
31644                        (first, second)
31645                    };
31646                    Ok(Some(Expression::Log(Box::new(LogFunc {
31647                        this: value,
31648                        base: Some(base),
31649                    }))))
31650                } else {
31651                    self.expect(TokenType::RParen)?;
31652                    if self.log_defaults_to_ln() {
31653                        Ok(Some(Expression::Ln(Box::new(UnaryFunc::new(first)))))
31654                    } else {
31655                        Ok(Some(Expression::Log(Box::new(LogFunc {
31656                            this: first,
31657                            base: None,
31658                        }))))
31659                    }
31660                }
31661            }
31662            (crate::function_registry::TypedParseKind::Variadic, "FLATTEN") => {
31663                let args = self.parse_function_arguments()?;
31664                self.expect(TokenType::RParen)?;
31665                Ok(Some(Expression::Function(Box::new(Function {
31666                    name: name.to_string(),
31667                    args,
31668                    distinct: false,
31669                    trailing_comments: Vec::new(),
31670                    use_bracket_syntax: false,
31671                    no_parens: false,
31672                    quoted: false,
31673                    span: None,
31674                    inferred_type: None,
31675                }))))
31676            }
31677            (crate::function_registry::TypedParseKind::Variadic, "ARRAY_INTERSECT") => {
31678                let mut expressions = vec![self.parse_expression()?];
31679                while self.match_token(TokenType::Comma) {
31680                    expressions.push(self.parse_expression()?);
31681                }
31682                self.expect(TokenType::RParen)?;
31683                Ok(Some(Expression::ArrayIntersect(Box::new(VarArgFunc {
31684                    expressions,
31685                    original_name: Some(name.to_string()),
31686                    inferred_type: None,
31687                }))))
31688            }
31689            (crate::function_registry::TypedParseKind::Variadic, "CURRENT_SCHEMAS") => {
31690                let args = if self.check(TokenType::RParen) {
31691                    Vec::new()
31692                } else {
31693                    vec![self.parse_expression()?]
31694                };
31695                self.expect(TokenType::RParen)?;
31696                Ok(Some(Expression::CurrentSchemas(Box::new(CurrentSchemas {
31697                    this: args.into_iter().next().map(Box::new),
31698                }))))
31699            }
31700            (crate::function_registry::TypedParseKind::Variadic, "COALESCE") => {
31701                let args = if self.check(TokenType::RParen) {
31702                    Vec::new()
31703                } else {
31704                    self.parse_expression_list()?
31705                };
31706                self.expect(TokenType::RParen)?;
31707                Ok(Some(Expression::Coalesce(Box::new(
31708                    crate::expressions::VarArgFunc {
31709                        original_name: None,
31710                        expressions: args,
31711                        inferred_type: None,
31712                    },
31713                ))))
31714            }
31715            (crate::function_registry::TypedParseKind::Variadic, "IFNULL") => {
31716                let args = self.parse_expression_list()?;
31717                self.expect(TokenType::RParen)?;
31718                if args.len() >= 2 {
31719                    Ok(Some(Expression::Coalesce(Box::new(
31720                        crate::expressions::VarArgFunc {
31721                            original_name: Some("IFNULL".to_string()),
31722                            expressions: args,
31723                            inferred_type: None,
31724                        },
31725                    ))))
31726                } else {
31727                    Ok(Some(Expression::Function(Box::new(Function {
31728                        name: name.to_string(),
31729                        args,
31730                        distinct: false,
31731                        trailing_comments: Vec::new(),
31732                        use_bracket_syntax: false,
31733                        no_parens: false,
31734                        quoted: false,
31735                        span: None,
31736                        inferred_type: None,
31737                    }))))
31738                }
31739            }
31740            (crate::function_registry::TypedParseKind::Variadic, "NVL") => {
31741                let args = self.parse_expression_list()?;
31742                self.expect(TokenType::RParen)?;
31743                if args.len() > 2 {
31744                    Ok(Some(Expression::Function(Box::new(Function {
31745                        name: "COALESCE".to_string(),
31746                        args,
31747                        distinct: false,
31748                        trailing_comments: Vec::new(),
31749                        use_bracket_syntax: false,
31750                        no_parens: false,
31751                        quoted: false,
31752                        span: None,
31753                        inferred_type: None,
31754                    }))))
31755                } else if args.len() == 2 {
31756                    Ok(Some(Expression::Nvl(Box::new(
31757                        crate::expressions::BinaryFunc {
31758                            original_name: Some("NVL".to_string()),
31759                            this: args[0].clone(),
31760                            expression: args[1].clone(),
31761                            inferred_type: None,
31762                        },
31763                    ))))
31764                } else {
31765                    Ok(Some(Expression::Function(Box::new(Function {
31766                        name: name.to_string(),
31767                        args,
31768                        distinct: false,
31769                        trailing_comments: Vec::new(),
31770                        use_bracket_syntax: false,
31771                        no_parens: false,
31772                        quoted: false,
31773                        span: None,
31774                        inferred_type: None,
31775                    }))))
31776                }
31777            }
31778            (crate::function_registry::TypedParseKind::Variadic, "NVL2") => {
31779                let args = self.parse_expression_list()?;
31780                self.expect(TokenType::RParen)?;
31781                if args.len() >= 3 {
31782                    Ok(Some(Expression::Nvl2(Box::new(
31783                        crate::expressions::Nvl2Func {
31784                            this: args[0].clone(),
31785                            true_value: args[1].clone(),
31786                            false_value: args[2].clone(),
31787                            inferred_type: None,
31788                        },
31789                    ))))
31790                } else {
31791                    Ok(Some(Expression::Function(Box::new(Function {
31792                        name: name.to_string(),
31793                        args,
31794                        distinct: false,
31795                        trailing_comments: Vec::new(),
31796                        use_bracket_syntax: false,
31797                        no_parens: false,
31798                        quoted: false,
31799                        span: None,
31800                        inferred_type: None,
31801                    }))))
31802                }
31803            }
31804            (crate::function_registry::TypedParseKind::Variadic, "EXTRACT") => {
31805                if matches!(
31806                    self.config.dialect,
31807                    Some(crate::dialects::DialectType::ClickHouse)
31808                ) && (self.check(TokenType::Identifier)
31809                    || self.check(TokenType::Var)
31810                    || self.peek().token_type.is_keyword()
31811                    || self.check(TokenType::String)
31812                    || self.check(TokenType::Number))
31813                    && (self.check_next(TokenType::Comma)
31814                        || self.check_next(TokenType::LParen)
31815                        || self.check_next(TokenType::Var)
31816                        || self.check_next(TokenType::Identifier))
31817                {
31818                    let args = self.parse_function_arguments()?;
31819                    self.expect(TokenType::RParen)?;
31820                    return Ok(Some(Expression::Function(Box::new(Function {
31821                        name: name.to_string(),
31822                        args,
31823                        distinct: false,
31824                        trailing_comments: Vec::new(),
31825                        use_bracket_syntax: false,
31826                        no_parens: false,
31827                        quoted: false,
31828                        span: None,
31829                        inferred_type: None,
31830                    }))));
31831                }
31832
31833                if self.check(TokenType::String) {
31834                    let args = self.parse_expression_list()?;
31835                    self.expect(TokenType::RParen)?;
31836                    return Ok(Some(Expression::Function(Box::new(Function {
31837                        name: name.to_string(),
31838                        args,
31839                        distinct: false,
31840                        trailing_comments: Vec::new(),
31841                        use_bracket_syntax: false,
31842                        no_parens: false,
31843                        quoted: false,
31844                        span: None,
31845                        inferred_type: None,
31846                    }))));
31847                }
31848
31849                let field = self.parse_datetime_field()?;
31850                if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
31851                    return Err(self.parse_error("Expected FROM or comma after EXTRACT field"));
31852                }
31853                let this = self.parse_expression()?;
31854                let this = self.try_clickhouse_func_arg_alias(this);
31855                self.expect(TokenType::RParen)?;
31856                Ok(Some(Expression::Extract(Box::new(ExtractFunc {
31857                    this,
31858                    field,
31859                }))))
31860            }
31861            (crate::function_registry::TypedParseKind::Variadic, "STRUCT") => {
31862                let args = if self.check(TokenType::RParen) {
31863                    Vec::new()
31864                } else {
31865                    self.parse_struct_args()?
31866                };
31867                self.expect(TokenType::RParen)?;
31868                Ok(Some(Expression::Function(Box::new(Function {
31869                    name: name.to_string(),
31870                    args,
31871                    distinct: false,
31872                    trailing_comments: Vec::new(),
31873                    use_bracket_syntax: false,
31874                    no_parens: false,
31875                    quoted: false,
31876                    span: None,
31877                    inferred_type: None,
31878                }))))
31879            }
31880            (crate::function_registry::TypedParseKind::Variadic, "CHAR") => {
31881                let args = self.parse_expression_list()?;
31882                let charset = if self.match_token(TokenType::Using) {
31883                    if !self.is_at_end() {
31884                        let charset_token = self.advance();
31885                        Some(charset_token.text.clone())
31886                    } else {
31887                        None
31888                    }
31889                } else {
31890                    None
31891                };
31892                self.expect(TokenType::RParen)?;
31893                if charset.is_some() {
31894                    Ok(Some(Expression::CharFunc(Box::new(
31895                        crate::expressions::CharFunc {
31896                            args,
31897                            charset,
31898                            name: None,
31899                        },
31900                    ))))
31901                } else {
31902                    Ok(Some(Expression::Function(Box::new(Function {
31903                        name: name.to_string(),
31904                        args,
31905                        distinct: false,
31906                        trailing_comments: Vec::new(),
31907                        use_bracket_syntax: false,
31908                        no_parens: false,
31909                        quoted: false,
31910                        span: None,
31911                        inferred_type: None,
31912                    }))))
31913                }
31914            }
31915            (crate::function_registry::TypedParseKind::Variadic, "CHR") => {
31916                let args = self.parse_expression_list()?;
31917                let charset = if self.match_token(TokenType::Using) {
31918                    if !self.is_at_end() {
31919                        let charset_token = self.advance();
31920                        Some(charset_token.text.clone())
31921                    } else {
31922                        None
31923                    }
31924                } else {
31925                    None
31926                };
31927                self.expect(TokenType::RParen)?;
31928                if charset.is_some() {
31929                    Ok(Some(Expression::CharFunc(Box::new(
31930                        crate::expressions::CharFunc {
31931                            args,
31932                            charset,
31933                            name: Some("CHR".to_string()),
31934                        },
31935                    ))))
31936                } else {
31937                    Ok(Some(Expression::Function(Box::new(Function {
31938                        name: name.to_string(),
31939                        args,
31940                        distinct: false,
31941                        trailing_comments: Vec::new(),
31942                        use_bracket_syntax: false,
31943                        no_parens: false,
31944                        quoted: false,
31945                        span: None,
31946                        inferred_type: None,
31947                    }))))
31948                }
31949            }
31950            (crate::function_registry::TypedParseKind::Variadic, "RANGE_N") => {
31951                let this = self.parse_bitwise_or()?;
31952                self.expect(TokenType::Between)?;
31953                let mut expressions = Vec::new();
31954                while !self.check(TokenType::Each) && !self.check(TokenType::RParen) {
31955                    expressions.push(self.parse_expression()?);
31956                    if !self.match_token(TokenType::Comma) {
31957                        break;
31958                    }
31959                }
31960                let each = if self.match_token(TokenType::Each) {
31961                    Some(Box::new(self.parse_expression()?))
31962                } else {
31963                    None
31964                };
31965                self.expect(TokenType::RParen)?;
31966                Ok(Some(Expression::RangeN(Box::new(RangeN {
31967                    this: Box::new(this),
31968                    expressions,
31969                    each,
31970                }))))
31971            }
31972            (crate::function_registry::TypedParseKind::Variadic, "XMLTABLE") => {
31973                if let Some(xml_table) = self.parse_xml_table()? {
31974                    self.expect(TokenType::RParen)?;
31975                    Ok(Some(xml_table))
31976                } else {
31977                    Err(self.parse_error("Failed to parse XMLTABLE"))
31978                }
31979            }
31980            (crate::function_registry::TypedParseKind::Variadic, "XMLELEMENT") => {
31981                if let Some(elem) = self.parse_xml_element()? {
31982                    self.expect(TokenType::RParen)?;
31983                    Ok(Some(elem))
31984                } else {
31985                    self.expect(TokenType::RParen)?;
31986                    Ok(Some(Expression::Function(Box::new(Function {
31987                        name: name.to_string(),
31988                        args: Vec::new(),
31989                        distinct: false,
31990                        trailing_comments: Vec::new(),
31991                        use_bracket_syntax: false,
31992                        no_parens: false,
31993                        quoted: false,
31994                        span: None,
31995                        inferred_type: None,
31996                    }))))
31997                }
31998            }
31999            (crate::function_registry::TypedParseKind::Variadic, "XMLATTRIBUTES") => {
32000                let mut attrs = Vec::new();
32001                if !self.check(TokenType::RParen) {
32002                    loop {
32003                        let expr = self.parse_expression()?;
32004                        if self.match_token(TokenType::As) {
32005                            let alias_ident = self.expect_identifier_or_keyword_with_quoted()?;
32006                            attrs.push(Expression::Alias(Box::new(Alias {
32007                                this: expr,
32008                                alias: alias_ident,
32009                                column_aliases: Vec::new(),
32010                                pre_alias_comments: Vec::new(),
32011                                trailing_comments: Vec::new(),
32012                                inferred_type: None,
32013                            })));
32014                        } else {
32015                            attrs.push(expr);
32016                        }
32017                        if !self.match_token(TokenType::Comma) {
32018                            break;
32019                        }
32020                    }
32021                }
32022                self.expect(TokenType::RParen)?;
32023                Ok(Some(Expression::Function(Box::new(Function {
32024                    name: "XMLATTRIBUTES".to_string(),
32025                    args: attrs,
32026                    distinct: false,
32027                    trailing_comments: Vec::new(),
32028                    use_bracket_syntax: false,
32029                    no_parens: false,
32030                    quoted: false,
32031                    span: None,
32032                    inferred_type: None,
32033                }))))
32034            }
32035            (crate::function_registry::TypedParseKind::Variadic, "XMLCOMMENT") => {
32036                let args = if self.check(TokenType::RParen) {
32037                    Vec::new()
32038                } else {
32039                    self.parse_expression_list()?
32040                };
32041                self.expect(TokenType::RParen)?;
32042                Ok(Some(Expression::Function(Box::new(Function {
32043                    name: "XMLCOMMENT".to_string(),
32044                    args,
32045                    distinct: false,
32046                    trailing_comments: Vec::new(),
32047                    use_bracket_syntax: false,
32048                    no_parens: false,
32049                    quoted: false,
32050                    span: None,
32051                    inferred_type: None,
32052                }))))
32053            }
32054            (crate::function_registry::TypedParseKind::Variadic, "MATCH") => {
32055                let expressions = if self.check(TokenType::Table)
32056                    && !matches!(
32057                        self.config.dialect,
32058                        Some(crate::dialects::DialectType::ClickHouse)
32059                    ) {
32060                    self.skip();
32061                    let table_name = self.expect_identifier_or_keyword()?;
32062                    vec![Expression::Var(Box::new(Var {
32063                        this: format!("TABLE {}", table_name),
32064                    }))]
32065                } else {
32066                    self.parse_expression_list()?
32067                };
32068
32069                self.expect(TokenType::RParen)?;
32070
32071                if !self.check_keyword_text("AGAINST") {
32072                    return Ok(Some(Expression::Function(Box::new(Function {
32073                        name: "MATCH".to_string(),
32074                        args: expressions,
32075                        distinct: false,
32076                        trailing_comments: Vec::new(),
32077                        use_bracket_syntax: false,
32078                        no_parens: false,
32079                        quoted: false,
32080                        span: None,
32081                        inferred_type: None,
32082                    }))));
32083                }
32084
32085                self.skip();
32086                self.expect(TokenType::LParen)?;
32087                let search_expr = self.parse_primary()?;
32088
32089                let modifier = if self.match_text_seq(&["IN", "NATURAL", "LANGUAGE", "MODE"]) {
32090                    if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
32091                        Some(Box::new(Expression::Var(Box::new(Var {
32092                            this: "IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION".to_string(),
32093                        }))))
32094                    } else {
32095                        Some(Box::new(Expression::Var(Box::new(Var {
32096                            this: "IN NATURAL LANGUAGE MODE".to_string(),
32097                        }))))
32098                    }
32099                } else if self.match_text_seq(&["IN", "BOOLEAN", "MODE"]) {
32100                    Some(Box::new(Expression::Var(Box::new(Var {
32101                        this: "IN BOOLEAN MODE".to_string(),
32102                    }))))
32103                } else if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
32104                    Some(Box::new(Expression::Var(Box::new(Var {
32105                        this: "WITH QUERY EXPANSION".to_string(),
32106                    }))))
32107                } else {
32108                    None
32109                };
32110
32111                self.expect(TokenType::RParen)?;
32112
32113                Ok(Some(Expression::MatchAgainst(Box::new(MatchAgainst {
32114                    this: Box::new(search_expr),
32115                    expressions,
32116                    modifier,
32117                }))))
32118            }
32119            (crate::function_registry::TypedParseKind::Variadic, "TRANSFORM") => {
32120                let expressions = if self.check(TokenType::RParen) {
32121                    Vec::new()
32122                } else {
32123                    self.parse_function_args_with_lambda()?
32124                };
32125                self.expect(TokenType::RParen)?;
32126
32127                let row_format_before = if self.match_token(TokenType::Row) {
32128                    self.parse_row()?
32129                } else {
32130                    None
32131                };
32132
32133                let record_writer = if self.match_text_seq(&["RECORDWRITER"]) {
32134                    Some(Box::new(self.parse_expression()?))
32135                } else {
32136                    None
32137                };
32138
32139                if self.match_token(TokenType::Using) {
32140                    let command_script = Some(Box::new(self.parse_expression()?));
32141                    let schema = if self.match_token(TokenType::As) {
32142                        self.parse_schema()?
32143                    } else {
32144                        None
32145                    };
32146
32147                    let row_format_after = if self.match_token(TokenType::Row) {
32148                        self.parse_row()?
32149                    } else {
32150                        None
32151                    };
32152
32153                    let record_reader = if self.match_text_seq(&["RECORDREADER"]) {
32154                        Some(Box::new(self.parse_expression()?))
32155                    } else {
32156                        None
32157                    };
32158
32159                    Ok(Some(Expression::QueryTransform(Box::new(QueryTransform {
32160                        expressions,
32161                        command_script,
32162                        schema: schema.map(Box::new),
32163                        row_format_before: row_format_before.map(Box::new),
32164                        record_writer,
32165                        row_format_after: row_format_after.map(Box::new),
32166                        record_reader,
32167                    }))))
32168                } else {
32169                    Ok(Some(Expression::Function(Box::new(Function {
32170                        name: name.to_string(),
32171                        args: expressions,
32172                        distinct: false,
32173                        trailing_comments: Vec::new(),
32174                        use_bracket_syntax: false,
32175                        no_parens: false,
32176                        quoted,
32177                        span: None,
32178                        inferred_type: None,
32179                    }))))
32180                }
32181            }
32182            (crate::function_registry::TypedParseKind::Variadic, "CONVERT") => {
32183                let is_try = upper_name == "TRY_CONVERT";
32184                let is_tsql = matches!(
32185                    self.config.dialect,
32186                    Some(crate::dialects::DialectType::TSQL)
32187                        | Some(crate::dialects::DialectType::Fabric)
32188                );
32189
32190                if is_tsql {
32191                    let saved = self.current;
32192                    let orig_type_text = if self.current < self.tokens.len() {
32193                        self.tokens[self.current].text.to_ascii_uppercase()
32194                    } else {
32195                        String::new()
32196                    };
32197                    let dt = self.parse_data_type();
32198                    if let Ok(mut dt) = dt {
32199                        if self.match_token(TokenType::Comma) {
32200                            if orig_type_text == "NVARCHAR" || orig_type_text == "NCHAR" {
32201                                dt = match dt {
32202                                    crate::expressions::DataType::VarChar { length, .. } => {
32203                                        if let Some(len) = length {
32204                                            crate::expressions::DataType::Custom {
32205                                                name: format!("{}({})", orig_type_text, len),
32206                                            }
32207                                        } else {
32208                                            crate::expressions::DataType::Custom {
32209                                                name: orig_type_text.clone(),
32210                                            }
32211                                        }
32212                                    }
32213                                    crate::expressions::DataType::Char { length } => {
32214                                        if let Some(len) = length {
32215                                            crate::expressions::DataType::Custom {
32216                                                name: format!("{}({})", orig_type_text, len),
32217                                            }
32218                                        } else {
32219                                            crate::expressions::DataType::Custom {
32220                                                name: orig_type_text.clone(),
32221                                            }
32222                                        }
32223                                    }
32224                                    other => other,
32225                                };
32226                            }
32227                            let value = self.parse_expression()?;
32228                            let style = if self.match_token(TokenType::Comma) {
32229                                Some(self.parse_expression()?)
32230                            } else {
32231                                None
32232                            };
32233                            self.expect(TokenType::RParen)?;
32234                            let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
32235                            let mut args = vec![Expression::DataType(dt), value];
32236                            if let Some(s) = style {
32237                                args.push(s);
32238                            }
32239                            return Ok(Some(Expression::Function(Box::new(Function {
32240                                name: func_name.to_string(),
32241                                args,
32242                                distinct: false,
32243                                trailing_comments: Vec::new(),
32244                                use_bracket_syntax: false,
32245                                no_parens: false,
32246                                quoted: false,
32247                                span: None,
32248                                inferred_type: None,
32249                            }))));
32250                        }
32251                        self.current = saved;
32252                    } else {
32253                        self.current = saved;
32254                    }
32255                }
32256
32257                let this = self.parse_expression()?;
32258                if self.match_token(TokenType::Using) {
32259                    let charset = self.expect_identifier()?;
32260                    self.expect(TokenType::RParen)?;
32261                    Ok(Some(Expression::Cast(Box::new(Cast {
32262                        this,
32263                        to: DataType::CharacterSet { name: charset },
32264                        trailing_comments: Vec::new(),
32265                        double_colon_syntax: false,
32266                        format: None,
32267                        default: None,
32268                        inferred_type: None,
32269                    }))))
32270                } else if self.match_token(TokenType::Comma) {
32271                    let mut args = vec![this];
32272                    args.push(self.parse_expression()?);
32273                    while self.match_token(TokenType::Comma) {
32274                        args.push(self.parse_expression()?);
32275                    }
32276                    self.expect(TokenType::RParen)?;
32277                    let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
32278                    Ok(Some(Expression::Function(Box::new(Function {
32279                        name: func_name.to_string(),
32280                        args,
32281                        distinct: false,
32282                        trailing_comments: Vec::new(),
32283                        use_bracket_syntax: false,
32284                        no_parens: false,
32285                        quoted: false,
32286                        span: None,
32287                        inferred_type: None,
32288                    }))))
32289                } else {
32290                    self.expect(TokenType::RParen)?;
32291                    let func_name = if is_try { "TRY_CONVERT" } else { "CONVERT" };
32292                    Ok(Some(Expression::Function(Box::new(Function {
32293                        name: func_name.to_string(),
32294                        args: vec![this],
32295                        distinct: false,
32296                        trailing_comments: Vec::new(),
32297                        use_bracket_syntax: false,
32298                        no_parens: false,
32299                        quoted: false,
32300                        span: None,
32301                        inferred_type: None,
32302                    }))))
32303                }
32304            }
32305            (crate::function_registry::TypedParseKind::Variadic, "TRIM") => {
32306                let (position, position_explicit) = if self.match_token(TokenType::Leading) {
32307                    (TrimPosition::Leading, true)
32308                } else if self.match_token(TokenType::Trailing) {
32309                    (TrimPosition::Trailing, true)
32310                } else if self.match_token(TokenType::Both) {
32311                    (TrimPosition::Both, true)
32312                } else {
32313                    (TrimPosition::Both, false)
32314                };
32315
32316                if position_explicit || self.check(TokenType::From) {
32317                    if self.match_token(TokenType::From) {
32318                        let this = self.parse_expression()?;
32319                        self.expect(TokenType::RParen)?;
32320                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
32321                            this,
32322                            characters: None,
32323                            position,
32324                            sql_standard_syntax: true,
32325                            position_explicit,
32326                        }))))
32327                    } else {
32328                        let first_expr = self.parse_bitwise_or()?;
32329                        let first_expr = self.try_clickhouse_func_arg_alias(first_expr);
32330                        if self.match_token(TokenType::From) {
32331                            let this = self.parse_bitwise_or()?;
32332                            let this = self.try_clickhouse_func_arg_alias(this);
32333                            self.expect(TokenType::RParen)?;
32334                            Ok(Some(Expression::Trim(Box::new(TrimFunc {
32335                                this,
32336                                characters: Some(first_expr),
32337                                position,
32338                                sql_standard_syntax: true,
32339                                position_explicit,
32340                            }))))
32341                        } else {
32342                            self.expect(TokenType::RParen)?;
32343                            Ok(Some(Expression::Trim(Box::new(TrimFunc {
32344                                this: first_expr,
32345                                characters: None,
32346                                position,
32347                                sql_standard_syntax: true,
32348                                position_explicit,
32349                            }))))
32350                        }
32351                    }
32352                } else {
32353                    let first_expr = self.parse_expression()?;
32354                    let first_expr = self.try_clickhouse_func_arg_alias(first_expr);
32355                    if self.match_token(TokenType::From) {
32356                        let this = self.parse_expression()?;
32357                        let this = self.try_clickhouse_func_arg_alias(this);
32358                        self.expect(TokenType::RParen)?;
32359                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
32360                            this,
32361                            characters: Some(first_expr),
32362                            position: TrimPosition::Both,
32363                            sql_standard_syntax: true,
32364                            position_explicit: false,
32365                        }))))
32366                    } else if self.match_token(TokenType::Comma) {
32367                        let second_expr = self.parse_expression()?;
32368                        self.expect(TokenType::RParen)?;
32369                        let trim_pattern_first = matches!(
32370                            self.config.dialect,
32371                            Some(crate::dialects::DialectType::Spark)
32372                        );
32373                        let (this, characters) = if trim_pattern_first {
32374                            (second_expr, first_expr)
32375                        } else {
32376                            (first_expr, second_expr)
32377                        };
32378                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
32379                            this,
32380                            characters: Some(characters),
32381                            position: TrimPosition::Both,
32382                            sql_standard_syntax: false,
32383                            position_explicit: false,
32384                        }))))
32385                    } else {
32386                        self.expect(TokenType::RParen)?;
32387                        Ok(Some(Expression::Trim(Box::new(TrimFunc {
32388                            this: first_expr,
32389                            characters: None,
32390                            position: TrimPosition::Both,
32391                            sql_standard_syntax: false,
32392                            position_explicit: false,
32393                        }))))
32394                    }
32395                }
32396            }
32397            (crate::function_registry::TypedParseKind::Variadic, "OVERLAY") => {
32398                if matches!(
32399                    self.config.dialect,
32400                    Some(crate::dialects::DialectType::ClickHouse)
32401                ) {
32402                    let args = self.parse_function_arguments()?;
32403                    self.expect(TokenType::RParen)?;
32404                    return Ok(Some(Expression::Function(Box::new(Function {
32405                        name: name.to_string(),
32406                        args,
32407                        distinct: false,
32408                        trailing_comments: Vec::new(),
32409                        use_bracket_syntax: false,
32410                        no_parens: false,
32411                        quoted: false,
32412                        span: None,
32413                        inferred_type: None,
32414                    }))));
32415                }
32416
32417                let this = self.parse_expression()?;
32418                if self.match_token(TokenType::Placing) {
32419                    let replacement = self.parse_expression()?;
32420                    self.expect(TokenType::From)?;
32421                    let from = self.parse_expression()?;
32422                    let length = if self.match_token(TokenType::For) {
32423                        Some(self.parse_expression()?)
32424                    } else {
32425                        None
32426                    };
32427                    self.expect(TokenType::RParen)?;
32428                    Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
32429                        this,
32430                        replacement,
32431                        from,
32432                        length,
32433                    }))))
32434                } else if self.match_token(TokenType::Comma) {
32435                    let replacement = self.parse_expression()?;
32436                    if self.match_token(TokenType::Comma) {
32437                        let from = self.parse_expression()?;
32438                        let length = if self.match_token(TokenType::Comma) {
32439                            Some(self.parse_expression()?)
32440                        } else {
32441                            None
32442                        };
32443                        self.expect(TokenType::RParen)?;
32444                        Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
32445                            this,
32446                            replacement,
32447                            from,
32448                            length,
32449                        }))))
32450                    } else {
32451                        self.expect(TokenType::RParen)?;
32452                        Ok(Some(Expression::Function(Box::new(Function {
32453                            name: name.to_string(),
32454                            args: vec![this, replacement],
32455                            distinct: false,
32456                            trailing_comments: Vec::new(),
32457                            use_bracket_syntax: false,
32458                            no_parens: false,
32459                            quoted: false,
32460                            span: None,
32461                            inferred_type: None,
32462                        }))))
32463                    }
32464                } else {
32465                    self.expect(TokenType::RParen)?;
32466                    Ok(Some(Expression::Function(Box::new(Function {
32467                        name: name.to_string(),
32468                        args: vec![this],
32469                        distinct: false,
32470                        trailing_comments: Vec::new(),
32471                        use_bracket_syntax: false,
32472                        no_parens: false,
32473                        quoted: false,
32474                        span: None,
32475                        inferred_type: None,
32476                    }))))
32477                }
32478            }
32479            (crate::function_registry::TypedParseKind::Variadic, "CEIL") => {
32480                let this = self.parse_expression()?;
32481                // Check for TO unit syntax (Druid: CEIL(__time TO WEEK))
32482                let to = if self.match_token(TokenType::To) {
32483                    // Parse the time unit as a variable/identifier
32484                    self.parse_var()?
32485                } else {
32486                    None
32487                };
32488                let decimals = if to.is_none() && self.match_token(TokenType::Comma) {
32489                    Some(self.parse_expression()?)
32490                } else {
32491                    None
32492                };
32493                self.expect(TokenType::RParen)?;
32494                Ok(Some(Expression::Ceil(Box::new(CeilFunc {
32495                    this,
32496                    decimals,
32497                    to,
32498                }))))
32499            }
32500            (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_FROM_PARTS")
32501            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_NTZ_FROM_PARTS")
32502            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_LTZ_FROM_PARTS")
32503            | (crate::function_registry::TypedParseKind::Variadic, "TIMESTAMP_TZ_FROM_PARTS")
32504            | (crate::function_registry::TypedParseKind::Variadic, "DATE_FROM_PARTS")
32505            | (crate::function_registry::TypedParseKind::Variadic, "TIME_FROM_PARTS") => {
32506                let args = self.parse_expression_list()?;
32507                self.expect(TokenType::RParen)?;
32508                Ok(Some(Expression::Function(Box::new(Function {
32509                    name: name.to_string(),
32510                    args,
32511                    distinct: false,
32512                    trailing_comments: Vec::new(),
32513                    use_bracket_syntax: false,
32514                    no_parens: false,
32515                    quoted: false,
32516                    span: None,
32517                    inferred_type: None,
32518                }))))
32519            }
32520            (crate::function_registry::TypedParseKind::CastLike, "TRY_CAST") => {
32521                let this = self.parse_expression()?;
32522                self.expect(TokenType::As)?;
32523                let to = self.parse_data_type()?;
32524                self.expect(TokenType::RParen)?;
32525                Ok(Some(Expression::TryCast(Box::new(Cast {
32526                    this,
32527                    to,
32528                    trailing_comments: Vec::new(),
32529                    double_colon_syntax: false,
32530                    format: None,
32531                    default: None,
32532                    inferred_type: None,
32533                }))))
32534            }
32535            (crate::function_registry::TypedParseKind::Conditional, "IF") => {
32536                // ClickHouse: if() with zero args is valid in test queries
32537                if self.check(TokenType::RParen) {
32538                    self.skip();
32539                    return Ok(Some(Expression::Function(Box::new(Function {
32540                        name: name.to_string(),
32541                        args: vec![],
32542                        distinct: false,
32543                        trailing_comments: Vec::new(),
32544                        use_bracket_syntax: false,
32545                        no_parens: false,
32546                        quoted: false,
32547                        span: None,
32548                        inferred_type: None,
32549                    }))));
32550                }
32551                let args = self.parse_expression_list()?;
32552                self.expect(TokenType::RParen)?;
32553                let expr = if args.len() == 3 {
32554                    Expression::IfFunc(Box::new(crate::expressions::IfFunc {
32555                        original_name: Some(upper_name.to_string()),
32556                        condition: args[0].clone(),
32557                        true_value: args[1].clone(),
32558                        false_value: Some(args[2].clone()),
32559                        inferred_type: None,
32560                    }))
32561                } else if args.len() == 2 {
32562                    // IF with 2 args: condition, true_value (no false_value)
32563                    Expression::IfFunc(Box::new(crate::expressions::IfFunc {
32564                        original_name: Some(upper_name.to_string()),
32565                        condition: args[0].clone(),
32566                        true_value: args[1].clone(),
32567                        false_value: None,
32568                        inferred_type: None,
32569                    }))
32570                } else {
32571                    return Err(self.parse_error("IF function requires 2 or 3 arguments"));
32572                };
32573                Ok(Some(expr))
32574            }
32575            _ => {
32576                self.try_parse_registry_grouped_typed_family(name, upper_name, canonical_upper_name)
32577            }
32578        }
32579    }
32580
32581    /// Route heavy typed-function families via registry metadata groups.
32582    fn try_parse_registry_grouped_typed_family(
32583        &mut self,
32584        name: &str,
32585        upper_name: &str,
32586        canonical_upper_name: &str,
32587    ) -> Result<Option<Expression>> {
32588        use crate::function_registry::TypedDispatchGroup;
32589
32590        match crate::function_registry::typed_dispatch_group_by_name_upper(canonical_upper_name) {
32591            Some(TypedDispatchGroup::AggregateFamily) => self
32592                .parse_typed_aggregate_family(name, upper_name, canonical_upper_name)
32593                .map(Some),
32594            Some(TypedDispatchGroup::WindowFamily) => self
32595                .parse_typed_window_family(name, upper_name, canonical_upper_name)
32596                .map(Some),
32597            Some(TypedDispatchGroup::JsonFamily) => self
32598                .parse_typed_json_family(name, upper_name, canonical_upper_name)
32599                .map(Some),
32600            Some(TypedDispatchGroup::TranslateTeradataFamily) => {
32601                if matches!(
32602                    self.config.dialect,
32603                    Some(crate::dialects::DialectType::Teradata)
32604                ) {
32605                    self.parse_typed_translate_teradata_family(
32606                        name,
32607                        upper_name,
32608                        canonical_upper_name,
32609                    )
32610                    .map(Some)
32611                } else {
32612                    Ok(None)
32613                }
32614            }
32615            None => Ok(None),
32616        }
32617    }
32618
32619    fn make_unquoted_function(name: &str, args: Vec<Expression>) -> Expression {
32620        Expression::Function(Box::new(Function {
32621            name: name.to_string(),
32622            args,
32623            distinct: false,
32624            trailing_comments: Vec::new(),
32625            use_bracket_syntax: false,
32626            no_parens: false,
32627            quoted: false,
32628            span: None,
32629            inferred_type: None,
32630        }))
32631    }
32632
32633    fn make_simple_aggregate(
32634        name: &str,
32635        args: Vec<Expression>,
32636        distinct: bool,
32637        filter: Option<Expression>,
32638    ) -> Expression {
32639        Expression::AggregateFunction(Box::new(AggregateFunction {
32640            name: name.to_string(),
32641            args,
32642            distinct,
32643            filter,
32644            order_by: Vec::new(),
32645            limit: None,
32646            ignore_nulls: None,
32647            inferred_type: None,
32648        }))
32649    }
32650
32651    /// Parse phase-3 typed-function slices that are straightforward pass-throughs.
32652    fn try_parse_phase3_typed_function(
32653        &mut self,
32654        name: &str,
32655        _upper_name: &str,
32656        canonical_upper_name: &str,
32657    ) -> Result<Option<Expression>> {
32658        let Some(behavior) =
32659            crate::function_registry::parser_dispatch_behavior_by_name_upper(canonical_upper_name)
32660        else {
32661            return Ok(None);
32662        };
32663
32664        match behavior {
32665            crate::function_registry::ParserDispatchBehavior::ExprListFunction => {
32666                let args = self.parse_expression_list()?;
32667                self.expect(TokenType::RParen)?;
32668                Ok(Some(Self::make_unquoted_function(name, args)))
32669            }
32670            crate::function_registry::ParserDispatchBehavior::OptionalExprListFunction => {
32671                let args = if self.check(TokenType::RParen) {
32672                    Vec::new()
32673                } else {
32674                    self.parse_expression_list()?
32675                };
32676                self.expect(TokenType::RParen)?;
32677                Ok(Some(Self::make_unquoted_function(name, args)))
32678            }
32679            crate::function_registry::ParserDispatchBehavior::FunctionArgumentsFunction => {
32680                let args = self.parse_function_arguments()?;
32681                self.expect(TokenType::RParen)?;
32682                Ok(Some(Self::make_unquoted_function(name, args)))
32683            }
32684            crate::function_registry::ParserDispatchBehavior::ZeroArgFunction => {
32685                self.expect(TokenType::RParen)?;
32686                Ok(Some(Self::make_unquoted_function(name, Vec::new())))
32687            }
32688            crate::function_registry::ParserDispatchBehavior::ExprListMaybeAggregateByFilter => {
32689                let args = if self.check(TokenType::RParen) {
32690                    Vec::new()
32691                } else {
32692                    self.parse_expression_list()?
32693                };
32694                self.expect(TokenType::RParen)?;
32695                let filter = self.parse_filter_clause()?;
32696                if filter.is_some() {
32697                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
32698                } else {
32699                    Ok(Some(Self::make_unquoted_function(name, args)))
32700                }
32701            }
32702            crate::function_registry::ParserDispatchBehavior::ExprListMaybeAggregateByAggSuffix => {
32703                let args = self.parse_expression_list()?;
32704                self.expect(TokenType::RParen)?;
32705                let filter = self.parse_filter_clause()?;
32706                if canonical_upper_name.ends_with("_AGG") || filter.is_some() {
32707                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
32708                } else {
32709                    Ok(Some(Self::make_unquoted_function(name, args)))
32710                }
32711            }
32712            crate::function_registry::ParserDispatchBehavior::HashLike => {
32713                let args = self.parse_expression_list()?;
32714                self.expect(TokenType::RParen)?;
32715                let filter = self.parse_filter_clause()?;
32716                if canonical_upper_name == "HASH_AGG" || filter.is_some() {
32717                    Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
32718                } else {
32719                    Ok(Some(Self::make_unquoted_function(name, args)))
32720                }
32721            }
32722            crate::function_registry::ParserDispatchBehavior::HllAggregate => {
32723                let distinct = self.match_token(TokenType::Distinct);
32724                let args = if self.match_token(TokenType::Star) {
32725                    vec![Expression::Star(Star {
32726                        table: None,
32727                        except: None,
32728                        replace: None,
32729                        rename: None,
32730                        trailing_comments: Vec::new(),
32731                        span: None,
32732                    })]
32733                } else if self.check(TokenType::RParen) {
32734                    Vec::new()
32735                } else {
32736                    self.parse_expression_list()?
32737                };
32738                self.expect(TokenType::RParen)?;
32739                let filter = self.parse_filter_clause()?;
32740                Ok(Some(Self::make_simple_aggregate(
32741                    name, args, distinct, filter,
32742                )))
32743            }
32744            crate::function_registry::ParserDispatchBehavior::PercentileAggregate => {
32745                let distinct = self.match_token(TokenType::Distinct);
32746                if !distinct {
32747                    self.match_token(TokenType::All);
32748                }
32749                let args = self.parse_expression_list()?;
32750                self.expect(TokenType::RParen)?;
32751                let filter = self.parse_filter_clause()?;
32752                Ok(Some(Self::make_simple_aggregate(
32753                    name, args, distinct, filter,
32754                )))
32755            }
32756            crate::function_registry::ParserDispatchBehavior::ExprListAggregate => {
32757                let args = self.parse_expression_list()?;
32758                self.expect(TokenType::RParen)?;
32759                let filter = self.parse_filter_clause()?;
32760                Ok(Some(Self::make_simple_aggregate(name, args, false, filter)))
32761            }
32762            crate::function_registry::ParserDispatchBehavior::UnaryAggregate => {
32763                let this = self.parse_expression()?;
32764                self.expect(TokenType::RParen)?;
32765                let filter = self.parse_filter_clause()?;
32766                Ok(Some(Self::make_simple_aggregate(
32767                    name,
32768                    vec![this],
32769                    false,
32770                    filter,
32771                )))
32772            }
32773            crate::function_registry::ParserDispatchBehavior::TranslateNonTeradata => {
32774                if matches!(
32775                    self.config.dialect,
32776                    Some(crate::dialects::DialectType::Teradata)
32777                ) {
32778                    return Ok(None);
32779                }
32780                let args = self.parse_expression_list()?;
32781                self.expect(TokenType::RParen)?;
32782                Ok(Some(Self::make_unquoted_function(name, args)))
32783            }
32784        }
32785    }
32786
32787    /// Parse a typed function call (after the opening paren)
32788    /// Following Python SQLGlot pattern: match all function aliases to typed expressions
32789    fn parse_typed_function(
32790        &mut self,
32791        name: &str,
32792        upper_name: &str,
32793        quoted: bool,
32794    ) -> Result<Expression> {
32795        let canonical_upper_name =
32796            crate::function_registry::canonical_typed_function_name_upper(upper_name);
32797
32798        // Handle internal function rewrites (sqlglot internal functions that map to CAST)
32799        if canonical_upper_name == "TIME_TO_TIME_STR" {
32800            let arg = self.parse_expression()?;
32801            self.expect(TokenType::RParen)?;
32802            return Ok(Expression::Cast(Box::new(Cast {
32803                this: arg,
32804                to: DataType::Text,
32805                trailing_comments: Vec::new(),
32806                double_colon_syntax: false,
32807                format: None,
32808                default: None,
32809                inferred_type: None,
32810            })));
32811        }
32812
32813        if let Some(expr) =
32814            self.try_parse_registry_typed_function(name, upper_name, canonical_upper_name, quoted)?
32815        {
32816            return Ok(expr);
32817        }
32818        if let Some(expr) =
32819            self.try_parse_phase3_typed_function(name, upper_name, canonical_upper_name)?
32820        {
32821            return Ok(expr);
32822        }
32823
32824        self.parse_generic_function(name, quoted)
32825    }
32826
32827    fn parse_typed_aggregate_family(
32828        &mut self,
32829        name: &str,
32830        upper_name: &str,
32831        canonical_upper_name: &str,
32832    ) -> Result<Expression> {
32833        match canonical_upper_name {
32834            // COUNT function
32835            "COUNT" => {
32836                let (this, star, distinct) = if self.check(TokenType::RParen) {
32837                    (None, false, false)
32838                } else if self.match_token(TokenType::Star) {
32839                    (None, true, false)
32840                } else if self.match_token(TokenType::All) {
32841                    // COUNT(ALL expr) - ALL is the default, just consume it
32842                    (Some(self.parse_expression()?), false, false)
32843                } else if self.match_token(TokenType::Distinct) {
32844                    let first_expr = self.parse_expression()?;
32845                    // Check for multiple columns: COUNT(DISTINCT a, b, c)
32846                    if self.match_token(TokenType::Comma) {
32847                        let mut args = vec![first_expr];
32848                        loop {
32849                            args.push(self.parse_expression()?);
32850                            if !self.match_token(TokenType::Comma) {
32851                                break;
32852                            }
32853                        }
32854                        // Return as a tuple expression for COUNT DISTINCT over multiple columns
32855                        (
32856                            Some(Expression::Tuple(Box::new(Tuple { expressions: args }))),
32857                            false,
32858                            true,
32859                        )
32860                    } else {
32861                        (Some(first_expr), false, true)
32862                    }
32863                } else {
32864                    let first_expr = self.parse_expression()?;
32865                    // ClickHouse: consume optional AS alias inside function args (e.g., count(NULL AS a))
32866                    let first_expr = if matches!(
32867                        self.config.dialect,
32868                        Some(crate::dialects::DialectType::ClickHouse)
32869                    ) && self.check(TokenType::As)
32870                    {
32871                        self.skip(); // consume AS
32872                        let alias = self.expect_identifier_or_keyword_with_quoted()?;
32873                        Expression::Alias(Box::new(Alias {
32874                            this: first_expr,
32875                            alias,
32876                            column_aliases: Vec::new(),
32877                            pre_alias_comments: Vec::new(),
32878                            trailing_comments: Vec::new(),
32879                            inferred_type: None,
32880                        }))
32881                    } else {
32882                        first_expr
32883                    };
32884                    // Check for multiple arguments (rare but possible)
32885                    if self.match_token(TokenType::Comma) {
32886                        let mut args = vec![first_expr];
32887                        loop {
32888                            args.push(self.parse_expression()?);
32889                            if !self.match_token(TokenType::Comma) {
32890                                break;
32891                            }
32892                        }
32893                        self.expect(TokenType::RParen)?;
32894                        // Multiple args without DISTINCT - treat as generic function
32895                        return Ok(Expression::Function(Box::new(Function {
32896                            name: name.to_string(),
32897                            args,
32898                            distinct: false,
32899                            trailing_comments: Vec::new(),
32900                            use_bracket_syntax: false,
32901                            no_parens: false,
32902                            quoted: false,
32903                            span: None,
32904                            inferred_type: None,
32905                        })));
32906                    }
32907                    (Some(first_expr), false, false)
32908                };
32909                // BigQuery: RESPECT NULLS / IGNORE NULLS inside COUNT
32910                let ignore_nulls = if self.match_token(TokenType::Ignore)
32911                    && self.match_token(TokenType::Nulls)
32912                {
32913                    Some(true)
32914                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
32915                {
32916                    Some(false)
32917                } else {
32918                    None
32919                };
32920                self.expect(TokenType::RParen)?;
32921                let filter = self.parse_filter_clause()?;
32922                // Also check for IGNORE NULLS / RESPECT NULLS after the closing paren
32923                let ignore_nulls = if ignore_nulls.is_some() {
32924                    ignore_nulls
32925                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
32926                    Some(true)
32927                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
32928                    Some(false)
32929                } else {
32930                    None
32931                };
32932                Ok(Expression::Count(Box::new(CountFunc {
32933                    this,
32934                    star,
32935                    distinct,
32936                    filter,
32937                    ignore_nulls,
32938                    original_name: Some(name.to_string()),
32939                    inferred_type: None,
32940                })))
32941            }
32942
32943            // LIST function: LIST(SELECT ...) in Materialize - list constructor with subquery
32944            "LIST" => {
32945                let is_materialize = matches!(
32946                    self.config.dialect,
32947                    Some(crate::dialects::DialectType::Materialize)
32948                );
32949                if is_materialize && self.check(TokenType::Select) {
32950                    let query = self.parse_select()?;
32951                    self.expect(TokenType::RParen)?;
32952                    return Ok(Expression::List(Box::new(List {
32953                        expressions: vec![query],
32954                    })));
32955                }
32956                // For non-Materialize or non-subquery, parse as either generic function or aggregate.
32957                let distinct = self.match_token(TokenType::Distinct);
32958                let args = if self.check(TokenType::RParen) {
32959                    Vec::new()
32960                } else {
32961                    self.parse_function_arguments()?
32962                };
32963                let order_by = if self.match_token(TokenType::Order) {
32964                    self.expect(TokenType::By)?;
32965                    self.parse_order_by_list()?
32966                } else {
32967                    Vec::new()
32968                };
32969                let limit = if self.match_token(TokenType::Limit) {
32970                    Some(Box::new(self.parse_expression()?))
32971                } else {
32972                    None
32973                };
32974                self.expect(TokenType::RParen)?;
32975                let filter = self.parse_filter_clause()?;
32976
32977                if distinct || !order_by.is_empty() || limit.is_some() || filter.is_some() {
32978                    Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
32979                        name: name.to_string(),
32980                        args,
32981                        distinct,
32982                        filter,
32983                        order_by,
32984                        limit,
32985                        ignore_nulls: None,
32986                        inferred_type: None,
32987                    })))
32988                } else {
32989                    Ok(Expression::Function(Box::new(Function {
32990                        name: name.to_string(),
32991                        args,
32992                        distinct: false,
32993                        trailing_comments: Vec::new(),
32994                        use_bracket_syntax: false,
32995                        no_parens: false,
32996                        quoted: false,
32997                        span: None,
32998                        inferred_type: None,
32999                    })))
33000                }
33001            }
33002
33003            // MAP function: MAP(SELECT ...) in Materialize - map constructor with subquery
33004            "MAP" => {
33005                let is_materialize = matches!(
33006                    self.config.dialect,
33007                    Some(crate::dialects::DialectType::Materialize)
33008                );
33009                if is_materialize && self.check(TokenType::Select) {
33010                    let query = self.parse_select()?;
33011                    self.expect(TokenType::RParen)?;
33012                    return Ok(Expression::ToMap(Box::new(ToMap {
33013                        this: Box::new(query),
33014                    })));
33015                }
33016                // For non-Materialize or non-subquery, fall through to generic handling
33017                let args = if self.check(TokenType::RParen) {
33018                    Vec::new()
33019                } else {
33020                    self.parse_function_arguments()?
33021                };
33022                self.expect(TokenType::RParen)?;
33023                Ok(Expression::Function(Box::new(Function {
33024                    name: name.to_string(),
33025                    args,
33026                    distinct: false,
33027                    trailing_comments: Vec::new(),
33028                    use_bracket_syntax: false,
33029                    no_parens: false,
33030                    quoted: false,
33031                    span: None,
33032                    inferred_type: None,
33033                })))
33034            }
33035
33036            // ARRAY function: ARRAY(SELECT ...) or ARRAY((SELECT ...) LIMIT n) is an array constructor with subquery
33037            // Different from ARRAY<type> which is a data type
33038            "ARRAY" => {
33039                // Check if this is ARRAY(SELECT ...) - array subquery constructor
33040                if self.check(TokenType::Select) {
33041                    let query = self.parse_select()?;
33042                    self.expect(TokenType::RParen)?;
33043                    // Pass the query directly as an argument to ARRAY function
33044                    // The generator will handle it correctly
33045                    return Ok(Expression::Function(Box::new(Function {
33046                        name: name.to_string(),
33047                        args: vec![query],
33048                        distinct: false,
33049                        trailing_comments: Vec::new(),
33050                        use_bracket_syntax: false,
33051                        no_parens: false,
33052                        quoted: false,
33053                        span: None,
33054                        inferred_type: None,
33055                    })));
33056                }
33057                // Check if this is ARRAY((SELECT ...) LIMIT n) - BigQuery allows LIMIT outside the subquery parens
33058                // This is common for constructs like ARRAY((SELECT AS STRUCT ...) LIMIT 10)
33059                if self.check(TokenType::LParen) {
33060                    // This could be a parenthesized subquery with modifiers after it
33061                    // Save position in case we need to backtrack
33062                    let saved_pos = self.current;
33063                    self.skip(); // consume opening paren
33064
33065                    // Check if there's a SELECT or WITH inside
33066                    if self.check(TokenType::Select) || self.check(TokenType::With) {
33067                        let inner_query = self.parse_statement()?;
33068                        self.expect(TokenType::RParen)?; // close inner parens
33069
33070                        // Now check for LIMIT/OFFSET modifiers outside the inner parens
33071                        let limit = if self.match_token(TokenType::Limit) {
33072                            let expr = self.parse_expression()?;
33073                            Some(Limit {
33074                                this: expr,
33075                                percent: false,
33076                                comments: Vec::new(),
33077                            })
33078                        } else {
33079                            None
33080                        };
33081
33082                        let offset = if self.match_token(TokenType::Offset) {
33083                            let expr = self.parse_expression()?;
33084                            let rows = if self.match_token(TokenType::Row)
33085                                || self.match_token(TokenType::Rows)
33086                            {
33087                                Some(true)
33088                            } else {
33089                                None
33090                            };
33091                            Some(Offset { this: expr, rows })
33092                        } else {
33093                            None
33094                        };
33095
33096                        self.expect(TokenType::RParen)?; // close ARRAY parens
33097
33098                        // Wrap the inner query in a Subquery with the modifiers
33099                        let subquery = Expression::Subquery(Box::new(Subquery {
33100                            this: inner_query,
33101                            alias: None,
33102                            column_aliases: Vec::new(),
33103                            order_by: None,
33104                            limit,
33105                            offset,
33106                            lateral: false,
33107                            modifiers_inside: false,
33108                            trailing_comments: Vec::new(),
33109                            distribute_by: None,
33110                            sort_by: None,
33111                            cluster_by: None,
33112                            inferred_type: None,
33113                        }));
33114
33115                        return Ok(Expression::Function(Box::new(Function {
33116                            name: name.to_string(),
33117                            args: vec![subquery],
33118                            distinct: false,
33119                            trailing_comments: Vec::new(),
33120                            use_bracket_syntax: false,
33121                            no_parens: false,
33122                            quoted: false,
33123                            span: None,
33124                            inferred_type: None,
33125                        })));
33126                    } else {
33127                        // Not a subquery, backtrack and parse as regular arguments
33128                        self.current = saved_pos;
33129                    }
33130                }
33131                // Otherwise fall through to parse as generic function or error
33132                // This could be ARRAY(...values...) or invalid syntax
33133                let args = if self.check(TokenType::RParen) {
33134                    Vec::new()
33135                } else {
33136                    self.parse_function_arguments()?
33137                };
33138                self.expect(TokenType::RParen)?;
33139                Ok(Expression::Function(Box::new(Function {
33140                    name: name.to_string(),
33141                    args,
33142                    distinct: false,
33143                    trailing_comments: Vec::new(),
33144                    use_bracket_syntax: false,
33145                    no_parens: false,
33146                    quoted: false,
33147                    span: None,
33148                    inferred_type: None,
33149                })))
33150            }
33151
33152            // Simple aggregate functions (SUM, AVG, MIN, MAX, etc.)
33153            // These can have multiple arguments in some contexts (e.g., MAX(a, b) is a scalar function)
33154            "SUM"
33155            | "AVG"
33156            | "MIN"
33157            | "MAX"
33158            | "ARRAY_AGG"
33159            | "ARRAY_CONCAT_AGG"
33160            | "STDDEV"
33161            | "STDDEV_POP"
33162            | "STDDEV_SAMP"
33163            | "VARIANCE"
33164            | "VAR_POP"
33165            | "VAR_SAMP"
33166            | "MEDIAN"
33167            | "MODE"
33168            | "FIRST"
33169            | "LAST"
33170            | "ANY_VALUE"
33171            | "APPROX_DISTINCT"
33172            | "APPROX_COUNT_DISTINCT"
33173            | "BIT_AND"
33174            | "BIT_OR"
33175            | "BIT_XOR" => {
33176                let distinct = if self.match_token(TokenType::Distinct) {
33177                    true
33178                } else {
33179                    self.match_token(TokenType::All); // ALL is the default, just consume it
33180                    false
33181                };
33182
33183                // MODE() can have zero arguments when used with WITHIN GROUP
33184                // e.g., MODE() WITHIN GROUP (ORDER BY col)
33185                if self.check(TokenType::RParen) {
33186                    // Empty args - will likely be followed by WITHIN GROUP
33187                    self.expect(TokenType::RParen)?;
33188                    let filter = self.parse_filter_clause()?;
33189                    let agg = AggFunc {
33190                        ignore_nulls: None,
33191                        this: Expression::Null(Null {}), // Placeholder for 0-arg aggregate
33192                        distinct: false,
33193                        filter,
33194                        order_by: Vec::new(),
33195                        having_max: None,
33196                        name: Some(name.to_string()),
33197                        limit: None,
33198                        inferred_type: None,
33199                    };
33200                    return Ok(match upper_name {
33201                        "MODE" => Expression::Mode(Box::new(agg)),
33202                        _ => {
33203                            // ClickHouse: allow zero-arg aggregates (server will validate)
33204                            if matches!(
33205                                self.config.dialect,
33206                                Some(crate::dialects::DialectType::ClickHouse)
33207                            ) {
33208                                Expression::Function(Box::new(Function {
33209                                    name: name.to_string(),
33210                                    args: Vec::new(),
33211                                    distinct: false,
33212                                    trailing_comments: Vec::new(),
33213                                    use_bracket_syntax: false,
33214                                    no_parens: false,
33215                                    quoted: false,
33216                                    span: None,
33217                                    inferred_type: None,
33218                                }))
33219                            } else {
33220                                return Err(self.parse_error(format!(
33221                                    "{} cannot have zero arguments",
33222                                    upper_name
33223                                )));
33224                            }
33225                        }
33226                    });
33227                }
33228
33229                let first_arg = self.parse_expression_with_clickhouse_alias()?;
33230
33231                // Check if there are more arguments (multi-arg scalar function like MAX(a, b))
33232                if self.match_token(TokenType::Comma) {
33233                    // Special handling for FIRST, LAST, ANY_VALUE with boolean second arg
33234                    // In Spark/Hive: first(col, true) means FIRST(col) IGNORE NULLS
33235                    let is_ignore_nulls_func = matches!(upper_name, "FIRST" | "LAST" | "ANY_VALUE");
33236
33237                    let second_arg = self.parse_expression()?;
33238
33239                    // Check if this is the IGNORE NULLS pattern: func(col, true)
33240                    if is_ignore_nulls_func && self.check(TokenType::RParen) {
33241                        if let Expression::Boolean(BooleanLiteral { value: true }) = &second_arg {
33242                            // This is func(col, true) -> FUNC(col) IGNORE NULLS
33243                            self.expect(TokenType::RParen)?;
33244                            let filter = self.parse_filter_clause()?;
33245                            let agg = AggFunc {
33246                                ignore_nulls: Some(true),
33247                                this: first_arg,
33248                                distinct,
33249                                filter,
33250                                order_by: Vec::new(),
33251                                having_max: None,
33252                                name: Some(name.to_string()),
33253                                limit: None,
33254                                inferred_type: None,
33255                            };
33256                            return Ok(match upper_name {
33257                                "FIRST" => Expression::First(Box::new(agg)),
33258                                "LAST" => Expression::Last(Box::new(agg)),
33259                                "ANY_VALUE" => Expression::AnyValue(Box::new(agg)),
33260                                _ => unreachable!(
33261                                    "function name already matched by is_ignore_nulls_func guard"
33262                                ),
33263                            });
33264                        }
33265                    }
33266
33267                    // Multiple arguments - treat as generic function call
33268                    let mut args = vec![first_arg, second_arg];
33269                    while self.match_token(TokenType::Comma) {
33270                        args.push(self.parse_expression()?);
33271                    }
33272                    self.expect(TokenType::RParen)?;
33273                    Ok(Expression::Function(Box::new(Function {
33274                        name: name.to_string(),
33275                        args,
33276                        distinct: false,
33277                        trailing_comments: Vec::new(),
33278                        use_bracket_syntax: false,
33279                        no_parens: false,
33280                        quoted: false,
33281                        span: None,
33282                        inferred_type: None,
33283                    })))
33284                } else {
33285                    // Check for IGNORE NULLS / RESPECT NULLS (BigQuery style)
33286                    let ignore_nulls = if self.match_token(TokenType::Ignore)
33287                        && self.match_token(TokenType::Nulls)
33288                    {
33289                        Some(true)
33290                    } else if self.match_token(TokenType::Respect)
33291                        && self.match_token(TokenType::Nulls)
33292                    {
33293                        Some(false)
33294                    } else {
33295                        None
33296                    };
33297
33298                    // Check for HAVING MAX/MIN inside aggregate (BigQuery syntax)
33299                    // e.g., ANY_VALUE(fruit HAVING MAX sold)
33300                    let having_max = if self.match_token(TokenType::Having) {
33301                        let is_max = if self.check_keyword_text("MAX") {
33302                            self.skip();
33303                            true
33304                        } else if self.check_keyword_text("MIN") {
33305                            self.skip();
33306                            false
33307                        } else {
33308                            return Err(
33309                                self.parse_error("Expected MAX or MIN after HAVING in aggregate")
33310                            );
33311                        };
33312                        let expr = self.parse_expression()?;
33313                        Some((Box::new(expr), is_max))
33314                    } else {
33315                        None
33316                    };
33317
33318                    // Check for ORDER BY inside aggregate (e.g., ARRAY_AGG(x ORDER BY y))
33319                    let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33320                        self.parse_order_by_list()?
33321                    } else {
33322                        Vec::new()
33323                    };
33324                    // Check for LIMIT inside aggregate (e.g., ARRAY_AGG(x ORDER BY y LIMIT 2))
33325                    // Also supports LIMIT offset, count (e.g., ARRAY_AGG(x ORDER BY y LIMIT 1, 10))
33326                    let limit = if self.match_token(TokenType::Limit) {
33327                        let first = self.parse_expression()?;
33328                        if self.match_token(TokenType::Comma) {
33329                            let second = self.parse_expression()?;
33330                            // Store as Tuple(offset, count)
33331                            Some(Box::new(Expression::Tuple(Box::new(Tuple {
33332                                expressions: vec![first, second],
33333                            }))))
33334                        } else {
33335                            Some(Box::new(first))
33336                        }
33337                    } else {
33338                        None
33339                    };
33340                    // Single argument - treat as aggregate function
33341                    self.expect(TokenType::RParen)?;
33342                    let filter = self.parse_filter_clause()?;
33343                    // Also check for IGNORE NULLS / RESPECT NULLS after the closing paren
33344                    // e.g., FIRST(col) IGNORE NULLS (Hive/Spark/generic SQL syntax)
33345                    let ignore_nulls = if ignore_nulls.is_some() {
33346                        ignore_nulls
33347                    } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33348                        Some(true)
33349                    } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33350                        Some(false)
33351                    } else {
33352                        None
33353                    };
33354                    let agg = AggFunc {
33355                        ignore_nulls,
33356                        this: first_arg,
33357                        distinct,
33358                        filter,
33359                        order_by,
33360                        having_max,
33361                        name: Some(name.to_string()),
33362                        limit,
33363                        inferred_type: None,
33364                    };
33365                    Ok(match upper_name {
33366                        "SUM" => Expression::Sum(Box::new(agg)),
33367                        "AVG" => Expression::Avg(Box::new(agg)),
33368                        "MIN" => Expression::Min(Box::new(agg)),
33369                        "MAX" => Expression::Max(Box::new(agg)),
33370                        "ARRAY_AGG" => Expression::ArrayAgg(Box::new(agg)),
33371                        "ARRAY_CONCAT_AGG" => Expression::ArrayConcatAgg(Box::new(agg)),
33372                        "STDDEV" => Expression::Stddev(Box::new(agg)),
33373                        "STDDEV_POP" => Expression::StddevPop(Box::new(agg)),
33374                        "STDDEV_SAMP" => Expression::StddevSamp(Box::new(agg)),
33375                        "VARIANCE" => Expression::Variance(Box::new(agg)),
33376                        "VAR_POP" => Expression::VarPop(Box::new(agg)),
33377                        "VAR_SAMP" => Expression::VarSamp(Box::new(agg)),
33378                        "MEDIAN" => Expression::Median(Box::new(agg)),
33379                        "MODE" => Expression::Mode(Box::new(agg)),
33380                        "FIRST" => Expression::First(Box::new(agg)),
33381                        "LAST" => Expression::Last(Box::new(agg)),
33382                        "ANY_VALUE" => Expression::AnyValue(Box::new(agg)),
33383                        "APPROX_DISTINCT" => Expression::ApproxDistinct(Box::new(agg)),
33384                        "APPROX_COUNT_DISTINCT" => Expression::ApproxCountDistinct(Box::new(agg)),
33385                        "BIT_AND" => Expression::BitwiseAndAgg(Box::new(agg)),
33386                        "BIT_OR" => Expression::BitwiseOrAgg(Box::new(agg)),
33387                        "BIT_XOR" => Expression::BitwiseXorAgg(Box::new(agg)),
33388                        _ => unreachable!("aggregate function name already matched in caller"),
33389                    })
33390                }
33391            }
33392
33393            // STRING_AGG - STRING_AGG([DISTINCT] expr [, separator] [ORDER BY order_list])
33394            "STRING_AGG" => {
33395                let distinct = self.match_token(TokenType::Distinct);
33396                let this = self.parse_expression()?;
33397                // Separator is optional
33398                let separator = if self.match_token(TokenType::Comma) {
33399                    Some(self.parse_expression()?)
33400                } else {
33401                    None
33402                };
33403                let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33404                    Some(self.parse_order_by_list()?)
33405                } else {
33406                    None
33407                };
33408                // BigQuery: LIMIT inside STRING_AGG
33409                let limit = if self.match_token(TokenType::Limit) {
33410                    Some(Box::new(self.parse_expression()?))
33411                } else {
33412                    None
33413                };
33414                self.expect(TokenType::RParen)?;
33415                let filter = self.parse_filter_clause()?;
33416                Ok(Expression::StringAgg(Box::new(StringAggFunc {
33417                    this,
33418                    separator,
33419                    order_by,
33420                    distinct,
33421                    filter,
33422                    limit,
33423                    inferred_type: None,
33424                })))
33425            }
33426
33427            // GROUP_CONCAT - GROUP_CONCAT([DISTINCT] expr [, expr...] [ORDER BY order_list] [SEPARATOR 'sep'])
33428            // MySQL allows multiple args which get wrapped in CONCAT:
33429            // GROUP_CONCAT(a, b, c SEPARATOR ',') -> GroupConcat(CONCAT(a, b, c), SEPARATOR=',')
33430            "GROUP_CONCAT" => {
33431                let distinct = self.match_token(TokenType::Distinct);
33432                let first = self.parse_expression()?;
33433                // Check for additional comma-separated expressions (before ORDER BY or SEPARATOR)
33434                let mut exprs = vec![first];
33435                while self.match_token(TokenType::Comma) {
33436                    // Check if the next tokens are ORDER BY or SEPARATOR
33437                    // If so, the comma was part of the separator syntax (not more args)
33438                    if self.check(TokenType::Order) || self.check(TokenType::Separator) {
33439                        // This shouldn't happen normally in valid SQL, backtrack
33440                        break;
33441                    }
33442                    exprs.push(self.parse_expression()?);
33443                }
33444                // If multiple expressions, wrap in CONCAT (matches Python sqlglot behavior)
33445                let this = if exprs.len() == 1 {
33446                    exprs.pop().unwrap()
33447                } else {
33448                    Expression::Function(Box::new(Function::new("CONCAT".to_string(), exprs)))
33449                };
33450                // Parse optional ORDER BY
33451                let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33452                    Some(self.parse_order_by_list()?)
33453                } else {
33454                    None
33455                };
33456                // Parse optional SEPARATOR - can be a string literal or expression (e.g., variable)
33457                let separator = if self.match_token(TokenType::Separator) {
33458                    Some(self.parse_expression()?)
33459                } else {
33460                    None
33461                };
33462                self.expect(TokenType::RParen)?;
33463                let filter = self.parse_filter_clause()?;
33464                Ok(Expression::GroupConcat(Box::new(GroupConcatFunc {
33465                    this,
33466                    separator,
33467                    order_by,
33468                    distinct,
33469                    filter,
33470                    inferred_type: None,
33471                })))
33472            }
33473
33474            // LISTAGG - LISTAGG([DISTINCT] expr [, separator [ON OVERFLOW ...]]) WITHIN GROUP (ORDER BY ...)
33475            "LISTAGG" => {
33476                // Check for optional DISTINCT
33477                let distinct = self.match_token(TokenType::Distinct);
33478                let this = self.parse_expression()?;
33479                let separator = if self.match_token(TokenType::Comma) {
33480                    Some(self.parse_expression()?)
33481                } else {
33482                    None
33483                };
33484                // Parse optional ON OVERFLOW clause
33485                let on_overflow = if self.match_token(TokenType::On) {
33486                    if self.match_identifier("OVERFLOW") {
33487                        if self.match_identifier("ERROR") {
33488                            Some(ListAggOverflow::Error)
33489                        } else if self.match_token(TokenType::Truncate) {
33490                            // Optional filler string
33491                            let filler = if self.check(TokenType::String) {
33492                                Some(self.parse_expression()?)
33493                            } else {
33494                                None
33495                            };
33496                            // WITH COUNT or WITHOUT COUNT
33497                            let with_count = if self.match_token(TokenType::With) {
33498                                self.match_identifier("COUNT");
33499                                true
33500                            } else if self.match_identifier("WITHOUT") {
33501                                self.match_identifier("COUNT");
33502                                false
33503                            } else {
33504                                true // default is WITH COUNT
33505                            };
33506                            Some(ListAggOverflow::Truncate { filler, with_count })
33507                        } else {
33508                            None
33509                        }
33510                    } else {
33511                        None
33512                    }
33513                } else {
33514                    None
33515                };
33516                self.expect(TokenType::RParen)?;
33517                // WITHIN GROUP (ORDER BY ...) is handled by maybe_parse_over
33518                Ok(Expression::ListAgg(Box::new(ListAggFunc {
33519                    this,
33520                    separator,
33521                    on_overflow,
33522                    order_by: None,
33523                    distinct,
33524                    filter: None,
33525                    inferred_type: None,
33526                })))
33527            }
33528            _ => unreachable!(
33529                "phase-6 aggregate parser called with non-aggregate family name '{}'",
33530                canonical_upper_name
33531            ),
33532        }
33533    }
33534
33535    fn parse_typed_window_family(
33536        &mut self,
33537        name: &str,
33538        upper_name: &str,
33539        canonical_upper_name: &str,
33540    ) -> Result<Expression> {
33541        match canonical_upper_name {
33542            // Window functions with no arguments (ClickHouse allows args in row_number)
33543            "ROW_NUMBER" => {
33544                if self.check(TokenType::RParen) {
33545                    self.skip();
33546                    Ok(Expression::RowNumber(RowNumber))
33547                } else {
33548                    // ClickHouse: row_number(column1) — parse as regular function
33549                    let args = self.parse_function_args_list()?;
33550                    self.expect(TokenType::RParen)?;
33551                    let trailing_comments = self.previous_trailing_comments().to_vec();
33552                    Ok(Expression::Function(Box::new(Function {
33553                        name: name.to_string(),
33554                        args,
33555                        distinct: false,
33556                        trailing_comments,
33557                        use_bracket_syntax: false,
33558                        no_parens: false,
33559                        quoted: false,
33560                        span: None,
33561                        inferred_type: None,
33562                    })))
33563                }
33564            }
33565            "RANK" => {
33566                // DuckDB allows: RANK(ORDER BY col) OVER (...)
33567                // Oracle allows: RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
33568                let (order_by, args) = if self.check(TokenType::RParen) {
33569                    // RANK() - no arguments
33570                    (None, Vec::new())
33571                } else if self.match_token(TokenType::Order) {
33572                    // DuckDB: RANK(ORDER BY col)
33573                    self.expect(TokenType::By)?;
33574                    (Some(self.parse_order_by()?.expressions), Vec::new())
33575                } else {
33576                    // Oracle hypothetical: RANK(val1, val2, ...)
33577                    let mut args = vec![self.parse_expression()?];
33578                    while self.match_token(TokenType::Comma) {
33579                        args.push(self.parse_expression()?);
33580                    }
33581                    (None, args)
33582                };
33583                self.expect(TokenType::RParen)?;
33584                Ok(Expression::Rank(Rank { order_by, args }))
33585            }
33586            "DENSE_RANK" => {
33587                // Oracle allows: DENSE_RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
33588                let args = if self.check(TokenType::RParen) {
33589                    Vec::new()
33590                } else {
33591                    let mut args = vec![self.parse_expression()?];
33592                    while self.match_token(TokenType::Comma) {
33593                        args.push(self.parse_expression()?);
33594                    }
33595                    args
33596                };
33597                self.expect(TokenType::RParen)?;
33598                Ok(Expression::DenseRank(DenseRank { args }))
33599            }
33600            "PERCENT_RANK" => {
33601                // DuckDB allows: PERCENT_RANK(ORDER BY col) OVER (...)
33602                // Oracle allows: PERCENT_RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
33603                let (order_by, args) = if self.check(TokenType::RParen) {
33604                    // PERCENT_RANK() - no arguments
33605                    (None, Vec::new())
33606                } else if self.match_token(TokenType::Order) {
33607                    // DuckDB: PERCENT_RANK(ORDER BY col)
33608                    self.expect(TokenType::By)?;
33609                    (Some(self.parse_order_by()?.expressions), Vec::new())
33610                } else {
33611                    // Oracle hypothetical: PERCENT_RANK(val1, val2, ...)
33612                    let mut args = vec![self.parse_expression()?];
33613                    while self.match_token(TokenType::Comma) {
33614                        args.push(self.parse_expression()?);
33615                    }
33616                    (None, args)
33617                };
33618                self.expect(TokenType::RParen)?;
33619                Ok(Expression::PercentRank(PercentRank { order_by, args }))
33620            }
33621            "CUME_DIST" => {
33622                // DuckDB allows: CUME_DIST(ORDER BY col) OVER (...)
33623                // Oracle allows: CUME_DIST(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
33624                let (order_by, args) = if self.check(TokenType::RParen) {
33625                    // CUME_DIST() - no arguments
33626                    (None, Vec::new())
33627                } else if self.match_token(TokenType::Order) {
33628                    // DuckDB: CUME_DIST(ORDER BY col)
33629                    self.expect(TokenType::By)?;
33630                    (Some(self.parse_order_by()?.expressions), Vec::new())
33631                } else {
33632                    // Oracle hypothetical: CUME_DIST(val1, val2, ...)
33633                    let mut args = vec![self.parse_expression()?];
33634                    while self.match_token(TokenType::Comma) {
33635                        args.push(self.parse_expression()?);
33636                    }
33637                    (None, args)
33638                };
33639                self.expect(TokenType::RParen)?;
33640                Ok(Expression::CumeDist(CumeDist { order_by, args }))
33641            }
33642
33643            // NTILE
33644            "NTILE" => {
33645                // num_buckets is optional (Databricks allows NTILE() with no args)
33646                let num_buckets = if self.check(TokenType::RParen) {
33647                    None
33648                } else {
33649                    Some(self.parse_expression()?)
33650                };
33651
33652                // ClickHouse: NTILE can have extra args (e.g., ntile(3, 2)) — skip them
33653                while matches!(
33654                    self.config.dialect,
33655                    Some(crate::dialects::DialectType::ClickHouse)
33656                ) && self.match_token(TokenType::Comma)
33657                {
33658                    let _ = self.parse_expression()?;
33659                }
33660
33661                // DuckDB allows: NTILE(n ORDER BY col) OVER (...)
33662                let order_by = if self.match_token(TokenType::Order) {
33663                    self.expect(TokenType::By)?;
33664                    Some(self.parse_order_by()?.expressions)
33665                } else {
33666                    None
33667                };
33668                self.expect(TokenType::RParen)?;
33669                Ok(Expression::NTile(Box::new(NTileFunc {
33670                    num_buckets,
33671                    order_by,
33672                })))
33673            }
33674
33675            // LEAD / LAG
33676            "LEAD" | "LAG" => {
33677                let this = self.parse_expression()?;
33678                let (offset, default) = if self.match_token(TokenType::Comma) {
33679                    let off = self.parse_expression()?;
33680                    let def = if self.match_token(TokenType::Comma) {
33681                        Some(self.parse_expression()?)
33682                    } else {
33683                        None
33684                    };
33685                    (Some(off), def)
33686                } else {
33687                    (None, None)
33688                };
33689                // Check for IGNORE NULLS / RESPECT NULLS inside parens (e.g., Redshift: LAG(x IGNORE NULLS))
33690                let ignore_nulls_inside = if self.match_token(TokenType::Ignore)
33691                    && self.match_token(TokenType::Nulls)
33692                {
33693                    Some(true)
33694                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
33695                {
33696                    Some(false)
33697                } else {
33698                    None
33699                };
33700                self.expect(TokenType::RParen)?;
33701                // Also check for IGNORE NULLS / RESPECT NULLS after parens
33702                let ignore_nulls = if ignore_nulls_inside.is_some() {
33703                    ignore_nulls_inside
33704                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33705                    Some(true)
33706                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33707                    Some(false)
33708                } else {
33709                    None
33710                };
33711                let func = LeadLagFunc {
33712                    this,
33713                    offset,
33714                    default,
33715                    ignore_nulls,
33716                };
33717                Ok(if upper_name == "LEAD" {
33718                    Expression::Lead(Box::new(func))
33719                } else {
33720                    Expression::Lag(Box::new(func))
33721                })
33722            }
33723
33724            // FIRST_VALUE / LAST_VALUE
33725            "FIRST_VALUE" | "LAST_VALUE" => {
33726                let this = self.parse_expression()?;
33727                // Parse ORDER BY inside parens (e.g., DuckDB: LAST_VALUE(x ORDER BY x))
33728                let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
33729                    self.parse_order_by_list()?
33730                } else {
33731                    Vec::new()
33732                };
33733                // Check for IGNORE NULLS / RESPECT NULLS inside the parens
33734                let mut ignore_nulls_inside = if self.match_token(TokenType::Ignore)
33735                    && self.match_token(TokenType::Nulls)
33736                {
33737                    Some(true)
33738                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
33739                {
33740                    Some(false) // RESPECT NULLS explicitly sets to false
33741                } else {
33742                    None
33743                };
33744                // Spark/Hive: first_value(col, true) means FIRST_VALUE(col) IGNORE NULLS
33745                if ignore_nulls_inside.is_none() && self.match_token(TokenType::Comma) {
33746                    let second_arg = self.parse_expression()?;
33747                    if let Expression::Boolean(BooleanLiteral { value: true }) = &second_arg {
33748                        ignore_nulls_inside = Some(true);
33749                    }
33750                    // If second arg is not true, just ignore it (not standard)
33751                }
33752                self.expect(TokenType::RParen)?;
33753                // Also check for IGNORE NULLS / RESPECT NULLS after the parens (some dialects use this syntax)
33754                let ignore_nulls: Option<bool> = if ignore_nulls_inside.is_some() {
33755                    ignore_nulls_inside
33756                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33757                    Some(true)
33758                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33759                    Some(false)
33760                } else {
33761                    None
33762                };
33763                let func = ValueFunc {
33764                    this,
33765                    ignore_nulls,
33766                    order_by,
33767                };
33768                Ok(if upper_name == "FIRST_VALUE" {
33769                    Expression::FirstValue(Box::new(func))
33770                } else {
33771                    Expression::LastValue(Box::new(func))
33772                })
33773            }
33774
33775            // NTH_VALUE
33776            "NTH_VALUE" => {
33777                let this = self.parse_expression()?;
33778                self.expect(TokenType::Comma)?;
33779                let offset = self.parse_expression()?;
33780                // Check for IGNORE NULLS / RESPECT NULLS inside the parens
33781                let ignore_nulls_inside = if self.match_token(TokenType::Ignore)
33782                    && self.match_token(TokenType::Nulls)
33783                {
33784                    Some(true)
33785                } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls)
33786                {
33787                    Some(false)
33788                } else {
33789                    None
33790                };
33791                self.expect(TokenType::RParen)?;
33792                // Check for Snowflake FROM FIRST / FROM LAST after the parens
33793                let from_first = if self.match_keywords(&[TokenType::From, TokenType::First]) {
33794                    Some(true)
33795                } else if self.match_keywords(&[TokenType::From, TokenType::Last]) {
33796                    Some(false)
33797                } else {
33798                    None
33799                };
33800                // Also check for IGNORE NULLS / RESPECT NULLS after the parens (and after FROM FIRST/LAST)
33801                let ignore_nulls: Option<bool> = if ignore_nulls_inside.is_some() {
33802                    ignore_nulls_inside
33803                } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
33804                    Some(true)
33805                } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
33806                    Some(false)
33807                } else {
33808                    None
33809                };
33810                Ok(Expression::NthValue(Box::new(NthValueFunc {
33811                    this,
33812                    offset,
33813                    ignore_nulls,
33814                    from_first,
33815                })))
33816            }
33817            _ => unreachable!(
33818                "phase-6 window parser called with non-window family name '{}'",
33819                canonical_upper_name
33820            ),
33821        }
33822    }
33823
33824    fn parse_typed_json_family(
33825        &mut self,
33826        name: &str,
33827        upper_name: &str,
33828        canonical_upper_name: &str,
33829    ) -> Result<Expression> {
33830        match canonical_upper_name {
33831            // JSON functions
33832            "JSON_EXTRACT" | "JSON_EXTRACT_SCALAR" | "JSON_QUERY" | "JSON_VALUE" => {
33833                let this = self.parse_expression()?;
33834                // Path is optional for some dialects (e.g., TSQL JSON_QUERY with 1 arg defaults to '$')
33835                let path = if self.match_token(TokenType::Comma) {
33836                    self.parse_expression()?
33837                } else {
33838                    // Default path is '$' when not provided
33839                    Expression::Literal(Box::new(Literal::String("$".to_string())))
33840                };
33841
33842                // SQLite JSON_EXTRACT supports multiple paths - check for additional paths
33843                // If multiple paths, use generic Function instead of typed expression
33844                if self.check(TokenType::Comma)
33845                    && !self.check_identifier("WITH")
33846                    && !self.check_identifier("WITHOUT")
33847                    && !self.check_identifier("KEEP")
33848                    && !self.check_identifier("OMIT")
33849                    && !self.check_identifier("NULL")
33850                    && !self.check_identifier("ERROR")
33851                    && !self.check_identifier("EMPTY")
33852                    && !self.check(TokenType::Returning)
33853                {
33854                    let mut args = vec![this, path];
33855                    while self.match_token(TokenType::Comma) {
33856                        args.push(self.parse_expression()?);
33857                    }
33858                    self.expect(TokenType::RParen)?;
33859                    let func_expr = Expression::Function(Box::new(Function {
33860                        name: name.to_string(),
33861                        args,
33862                        distinct: false,
33863                        trailing_comments: Vec::new(),
33864                        use_bracket_syntax: false,
33865                        no_parens: false,
33866                        quoted: false,
33867                        span: None,
33868                        inferred_type: None,
33869                    }));
33870                    // Exasol: JSON_EXTRACT(...) EMITS (col1 TYPE1, col2 TYPE2)
33871                    if matches!(
33872                        self.config.dialect,
33873                        Some(crate::dialects::DialectType::Exasol)
33874                    ) && self.check_identifier("EMITS")
33875                    {
33876                        self.skip(); // consume EMITS
33877                        if let Some(schema) = self.parse_schema()? {
33878                            return Ok(Expression::FunctionEmits(Box::new(FunctionEmits {
33879                                this: func_expr,
33880                                emits: schema,
33881                            })));
33882                        }
33883                    }
33884                    return Ok(func_expr);
33885                }
33886
33887                // Parse JSON_QUERY/JSON_VALUE options (Trino/Presto style)
33888                // Options: WITH/WITHOUT [CONDITIONAL|UNCONDITIONAL] [ARRAY] WRAPPER
33889                //          KEEP QUOTES / OMIT QUOTES [ON SCALAR STRING]
33890                //          NULL ON ERROR / ERROR ON ERROR / EMPTY ON ERROR
33891                //          RETURNING type
33892                let mut wrapper_option: Option<String> = None;
33893                let mut quotes_option: Option<String> = None;
33894                let mut on_scalar_string = false;
33895                let mut on_error: Option<String> = None;
33896                let mut returning: Option<DataType> = None;
33897
33898                // Keep parsing options until we see RParen
33899                while !self.check(TokenType::RParen) {
33900                    // WITH [CONDITIONAL|UNCONDITIONAL] [ARRAY] WRAPPER - match in order of specificity
33901                    if self.match_text_seq(&["WITH", "UNCONDITIONAL", "ARRAY", "WRAPPER"]) {
33902                        wrapper_option = Some("WITH UNCONDITIONAL ARRAY WRAPPER".to_string());
33903                    } else if self.match_text_seq(&["WITH", "CONDITIONAL", "ARRAY", "WRAPPER"]) {
33904                        wrapper_option = Some("WITH CONDITIONAL ARRAY WRAPPER".to_string());
33905                    } else if self.match_text_seq(&["WITH", "UNCONDITIONAL", "WRAPPER"]) {
33906                        wrapper_option = Some("WITH UNCONDITIONAL WRAPPER".to_string());
33907                    } else if self.match_text_seq(&["WITH", "CONDITIONAL", "WRAPPER"]) {
33908                        wrapper_option = Some("WITH CONDITIONAL WRAPPER".to_string());
33909                    } else if self.match_text_seq(&["WITH", "ARRAY", "WRAPPER"]) {
33910                        wrapper_option = Some("WITH ARRAY WRAPPER".to_string());
33911                    } else if self.match_text_seq(&["WITH", "WRAPPER"]) {
33912                        wrapper_option = Some("WITH WRAPPER".to_string());
33913                    // WITHOUT [CONDITIONAL] [ARRAY] WRAPPER
33914                    } else if self.match_text_seq(&["WITHOUT", "CONDITIONAL", "ARRAY", "WRAPPER"]) {
33915                        wrapper_option = Some("WITHOUT CONDITIONAL ARRAY WRAPPER".to_string());
33916                    } else if self.match_text_seq(&["WITHOUT", "CONDITIONAL", "WRAPPER"]) {
33917                        wrapper_option = Some("WITHOUT CONDITIONAL WRAPPER".to_string());
33918                    } else if self.match_text_seq(&["WITHOUT", "ARRAY", "WRAPPER"]) {
33919                        wrapper_option = Some("WITHOUT ARRAY WRAPPER".to_string());
33920                    } else if self.match_text_seq(&["WITHOUT", "WRAPPER"]) {
33921                        wrapper_option = Some("WITHOUT WRAPPER".to_string());
33922                    } else if self.match_text_seq(&["KEEP", "QUOTES"]) {
33923                        // KEEP QUOTES
33924                        quotes_option = Some("KEEP QUOTES".to_string());
33925                    } else if self.match_text_seq(&["OMIT", "QUOTES", "ON", "SCALAR", "STRING"]) {
33926                        // OMIT QUOTES ON SCALAR STRING
33927                        quotes_option = Some("OMIT QUOTES".to_string());
33928                        on_scalar_string = true;
33929                    } else if self.match_text_seq(&["OMIT", "QUOTES"]) {
33930                        // OMIT QUOTES
33931                        quotes_option = Some("OMIT QUOTES".to_string());
33932                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
33933                        on_error = Some("NULL ON ERROR".to_string());
33934                    } else if self.match_text_seq(&["ERROR", "ON", "ERROR"]) {
33935                        on_error = Some("ERROR ON ERROR".to_string());
33936                    } else if self.match_text_seq(&["EMPTY", "ON", "ERROR"]) {
33937                        on_error = Some("EMPTY ON ERROR".to_string());
33938                    } else if self.match_token(TokenType::Returning) {
33939                        // RETURNING type
33940                        returning = Some(self.parse_data_type()?);
33941                    } else {
33942                        // No more options recognized, break
33943                        break;
33944                    }
33945                }
33946
33947                self.expect(TokenType::RParen)?;
33948                let func = JsonExtractFunc {
33949                    this,
33950                    path,
33951                    returning,
33952                    arrow_syntax: false,
33953                    hash_arrow_syntax: false,
33954                    wrapper_option,
33955                    quotes_option,
33956                    on_scalar_string,
33957                    on_error,
33958                };
33959                Ok(match upper_name {
33960                    "JSON_EXTRACT" => Expression::JsonExtract(Box::new(func)),
33961                    "JSON_EXTRACT_SCALAR" => Expression::JsonExtractScalar(Box::new(func)),
33962                    "JSON_QUERY" => Expression::JsonQuery(Box::new(func)),
33963                    "JSON_VALUE" => Expression::JsonValue(Box::new(func)),
33964                    _ => unreachable!("JSON function name already matched in caller"),
33965                })
33966            }
33967            // JSON_KEYS, TO_JSON, PARSE_JSON etc. support additional args including named args (BigQuery)
33968            // e.g., JSON_KEYS(expr, depth, mode => 'lax'), TO_JSON(expr, stringify_wide_numbers => FALSE)
33969            // e.g., PARSE_JSON('{}', wide_number_mode => 'exact')
33970            "JSON_ARRAY_LENGTH" | "JSON_KEYS" | "JSON_TYPE" | "TO_JSON" | "PARSE_JSON" => {
33971                let this = self.parse_expression()?;
33972                // ClickHouse: expr AS alias inside function args
33973                let this = self.maybe_clickhouse_alias(this);
33974
33975                // Check for additional arguments (comma-separated, possibly named)
33976                if self.match_token(TokenType::Comma) {
33977                    // Has additional arguments - parse as generic Function to preserve all args
33978                    let mut all_args = vec![this];
33979                    let remaining = self.parse_function_arguments()?;
33980                    all_args.extend(remaining);
33981                    self.expect(TokenType::RParen)?;
33982                    Ok(Expression::Function(Box::new(Function {
33983                        name: name.to_string(),
33984                        args: all_args,
33985                        distinct: false,
33986                        trailing_comments: Vec::new(),
33987                        use_bracket_syntax: false,
33988                        no_parens: false,
33989                        quoted: false,
33990                        span: None,
33991                        inferred_type: None,
33992                    })))
33993                } else {
33994                    // Single argument - use typed expression
33995                    self.expect(TokenType::RParen)?;
33996                    let func = UnaryFunc::new(this);
33997                    Ok(match canonical_upper_name {
33998                        "JSON_ARRAY_LENGTH" => Expression::JsonArrayLength(Box::new(func)),
33999                        "JSON_KEYS" => Expression::JsonKeys(Box::new(func)),
34000                        "JSON_TYPE" => Expression::JsonType(Box::new(func)),
34001                        "TO_JSON" => Expression::ToJson(Box::new(func)),
34002                        "PARSE_JSON" => Expression::ParseJson(Box::new(func)),
34003                        _ => unreachable!("JSON function name already matched in caller"),
34004                    })
34005                }
34006            }
34007
34008            // JSON_OBJECT with SQL standard syntax: JSON_OBJECT('key': value, ...) or JSON_OBJECT(*)
34009            "JSON_OBJECT" => {
34010                let mut pairs = Vec::new();
34011                let mut star = false;
34012                if !self.check(TokenType::RParen) {
34013                    // Check for JSON_OBJECT(*) syntax
34014                    if self.check(TokenType::Star) && self.check_next(TokenType::RParen) {
34015                        self.skip(); // consume *
34016                        star = true;
34017                    } else {
34018                        loop {
34019                            // Check for KEY keyword for KEY 'key' IS value syntax (KEY is a keyword token)
34020                            let has_key_keyword = self.match_token(TokenType::Key);
34021                            // Parse key: try string first (for 'key' syntax), then column
34022                            let key = if let Some(s) = self.parse_string()? {
34023                                s
34024                            } else {
34025                                // Use parse_column for key to avoid interpreting colon as JSON path
34026                                self.parse_column()?.ok_or_else(|| {
34027                                    self.parse_error("Expected key expression in JSON_OBJECT")
34028                                })?
34029                            };
34030
34031                            // Support colon, VALUE keyword (identifier), and IS keyword (for KEY...IS syntax)
34032                            let has_separator = self.match_token(TokenType::Colon)
34033                                || self.match_identifier("VALUE")
34034                                || (has_key_keyword && self.match_token(TokenType::Is));
34035
34036                            if has_separator {
34037                                let value = self.parse_bitwise()?.ok_or_else(|| {
34038                                    self.parse_error("Expected value expression in JSON_OBJECT")
34039                                })?;
34040                                // Check for FORMAT JSON after value
34041                                let value_with_format = if self.match_text_seq(&["FORMAT", "JSON"])
34042                                {
34043                                    Expression::JSONFormat(Box::new(JSONFormat {
34044                                        this: Some(Box::new(value)),
34045                                        options: Vec::new(),
34046                                        is_json: None,
34047                                        to_json: None,
34048                                    }))
34049                                } else {
34050                                    value
34051                                };
34052                                pairs.push((key, value_with_format));
34053                            } else {
34054                                // Just key/value pairs without separator
34055                                if self.match_token(TokenType::Comma) {
34056                                    let value = self.parse_bitwise()?.ok_or_else(|| {
34057                                        self.parse_error("Expected value expression in JSON_OBJECT")
34058                                    })?;
34059                                    pairs.push((key, value));
34060                                } else {
34061                                    return Err(self
34062                                        .parse_error("Expected value expression in JSON_OBJECT"));
34063                                }
34064                            }
34065                            if !self.match_token(TokenType::Comma) {
34066                                break;
34067                            }
34068                        }
34069                    }
34070                }
34071                // Parse optional modifiers: NULL ON NULL, ABSENT ON NULL, WITH UNIQUE KEYS
34072                let null_handling = if self.match_token(TokenType::Null) {
34073                    self.match_token(TokenType::On);
34074                    self.match_token(TokenType::Null);
34075                    Some(JsonNullHandling::NullOnNull)
34076                } else if self.match_identifier("ABSENT") {
34077                    self.match_token(TokenType::On);
34078                    self.match_token(TokenType::Null);
34079                    Some(JsonNullHandling::AbsentOnNull)
34080                } else {
34081                    None
34082                };
34083                let with_unique_keys = if self.match_token(TokenType::With) {
34084                    self.match_token(TokenType::Unique);
34085                    self.match_identifier("KEYS");
34086                    true
34087                } else {
34088                    false
34089                };
34090                // Parse optional RETURNING clause: RETURNING type [FORMAT JSON] [ENCODING encoding]
34091                let (returning_type, format_json, encoding) = if self
34092                    .match_token(TokenType::Returning)
34093                {
34094                    let return_type = self.parse_data_type()?;
34095                    // Optional FORMAT JSON
34096                    let has_format_json = if self.match_token(TokenType::Format) {
34097                        // JSON might be a keyword or identifier
34098                        let _ = self.match_token(TokenType::Json) || self.match_identifier("JSON");
34099                        true
34100                    } else {
34101                        false
34102                    };
34103                    // Optional ENCODING encoding
34104                    let enc = if self.match_identifier("ENCODING") {
34105                        Some(self.expect_identifier_or_keyword()?)
34106                    } else {
34107                        None
34108                    };
34109                    (Some(return_type), has_format_json, enc)
34110                } else {
34111                    (None, false, None)
34112                };
34113                self.expect(TokenType::RParen)?;
34114                Ok(Expression::JsonObject(Box::new(JsonObjectFunc {
34115                    pairs,
34116                    null_handling,
34117                    with_unique_keys,
34118                    returning_type,
34119                    format_json,
34120                    encoding,
34121                    star,
34122                })))
34123            }
34124
34125            // JSON_ARRAY function with Oracle-specific options
34126            // JSON_ARRAY(expr [FORMAT JSON], ... [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
34127            "JSON_ARRAY" => {
34128                let mut expressions = Vec::new();
34129                if !self.check(TokenType::RParen) {
34130                    loop {
34131                        let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
34132                        // Check for FORMAT JSON after each expression
34133                        let expr_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
34134                            Expression::JSONFormat(Box::new(JSONFormat {
34135                                this: Some(Box::new(expr)),
34136                                options: Vec::new(),
34137                                is_json: None,
34138                                to_json: None,
34139                            }))
34140                        } else {
34141                            expr
34142                        };
34143                        expressions.push(expr_with_format);
34144                        if !self.match_token(TokenType::Comma) {
34145                            break;
34146                        }
34147                    }
34148                }
34149                // Parse NULL ON NULL or ABSENT ON NULL
34150                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
34151                    Some(Box::new(Expression::Var(Box::new(Var {
34152                        this: "NULL ON NULL".to_string(),
34153                    }))))
34154                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
34155                    Some(Box::new(Expression::Var(Box::new(Var {
34156                        this: "ABSENT ON NULL".to_string(),
34157                    }))))
34158                } else {
34159                    None
34160                };
34161                // Parse RETURNING type
34162                let return_type = if self.match_token(TokenType::Returning) {
34163                    let dt = self.parse_data_type()?;
34164                    Some(Box::new(Expression::DataType(dt)))
34165                } else {
34166                    None
34167                };
34168                // Parse STRICT
34169                let strict = if self.match_identifier("STRICT") {
34170                    Some(Box::new(Expression::Boolean(BooleanLiteral {
34171                        value: true,
34172                    })))
34173                } else {
34174                    None
34175                };
34176                self.expect(TokenType::RParen)?;
34177                Ok(Expression::JSONArray(Box::new(JSONArray {
34178                    expressions,
34179                    null_handling,
34180                    return_type,
34181                    strict,
34182                })))
34183            }
34184
34185            // JSON_ARRAYAGG function with Oracle-specific options
34186            // JSON_ARRAYAGG(expr [FORMAT JSON] [ORDER BY ...] [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
34187            "JSON_ARRAYAGG" => {
34188                let this = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
34189                // Check for FORMAT JSON after the expression
34190                let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
34191                    Expression::JSONFormat(Box::new(JSONFormat {
34192                        this: Some(Box::new(this)),
34193                        options: Vec::new(),
34194                        is_json: None,
34195                        to_json: None,
34196                    }))
34197                } else {
34198                    this
34199                };
34200                // Parse ORDER BY clause
34201                let order = if self.match_token(TokenType::Order) {
34202                    self.match_token(TokenType::By);
34203                    // Parse comma-separated ordered expressions
34204                    let mut order_exprs = Vec::new();
34205                    loop {
34206                        if let Some(ordered) = self.parse_ordered_item()? {
34207                            order_exprs.push(ordered);
34208                        } else {
34209                            break;
34210                        }
34211                        if !self.match_token(TokenType::Comma) {
34212                            break;
34213                        }
34214                    }
34215                    if !order_exprs.is_empty() {
34216                        Some(Box::new(Expression::OrderBy(Box::new(OrderBy {
34217                            expressions: order_exprs,
34218                            siblings: false,
34219                            comments: Vec::new(),
34220                        }))))
34221                    } else {
34222                        None
34223                    }
34224                } else {
34225                    None
34226                };
34227                // Parse NULL ON NULL or ABSENT ON NULL
34228                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
34229                    Some(Box::new(Expression::Var(Box::new(Var {
34230                        this: "NULL ON NULL".to_string(),
34231                    }))))
34232                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
34233                    Some(Box::new(Expression::Var(Box::new(Var {
34234                        this: "ABSENT ON NULL".to_string(),
34235                    }))))
34236                } else {
34237                    None
34238                };
34239                // Parse RETURNING type
34240                let return_type = if self.match_token(TokenType::Returning) {
34241                    let dt = self.parse_data_type()?;
34242                    Some(Box::new(Expression::DataType(dt)))
34243                } else {
34244                    None
34245                };
34246                // Parse STRICT
34247                let strict = if self.match_identifier("STRICT") {
34248                    Some(Box::new(Expression::Boolean(BooleanLiteral {
34249                        value: true,
34250                    })))
34251                } else {
34252                    None
34253                };
34254                self.expect(TokenType::RParen)?;
34255                Ok(Expression::JSONArrayAgg(Box::new(JSONArrayAgg {
34256                    this: Box::new(this_with_format),
34257                    order,
34258                    null_handling,
34259                    return_type,
34260                    strict,
34261                })))
34262            }
34263
34264            // JSON_OBJECTAGG with KEY...VALUE syntax
34265            // JSON_OBJECTAGG(KEY key VALUE value) or JSON_OBJECTAGG(key: value)
34266            "JSON_OBJECTAGG" => {
34267                // Check for KEY keyword (KEY is a keyword token, not an identifier)
34268                let _has_key_keyword = self.match_token(TokenType::Key);
34269                // Parse key: use column parsing to avoid colon being interpreted as JSON path
34270                let key = self.parse_column()?.unwrap_or(Expression::Null(Null));
34271
34272                // Support colon, comma (MySQL), or VALUE keyword
34273                let _ = self.match_token(TokenType::Colon)
34274                    || self.match_token(TokenType::Comma)
34275                    || self.match_identifier("VALUE");
34276
34277                let value = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
34278                // Check for FORMAT JSON after value
34279                let value_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
34280                    Expression::JSONFormat(Box::new(JSONFormat {
34281                        this: Some(Box::new(value)),
34282                        options: Vec::new(),
34283                        is_json: None,
34284                        to_json: None,
34285                    }))
34286                } else {
34287                    value
34288                };
34289                // Parse NULL ON NULL or ABSENT ON NULL
34290                let null_handling = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
34291                    Some(Box::new(Expression::Var(Box::new(Var {
34292                        this: "NULL ON NULL".to_string(),
34293                    }))))
34294                } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
34295                    Some(Box::new(Expression::Var(Box::new(Var {
34296                        this: "ABSENT ON NULL".to_string(),
34297                    }))))
34298                } else {
34299                    None
34300                };
34301                // Parse WITH/WITHOUT UNIQUE KEYS
34302                let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE"]) {
34303                    self.match_identifier("KEYS");
34304                    Some(Box::new(Expression::Boolean(BooleanLiteral {
34305                        value: true,
34306                    })))
34307                } else if self.match_text_seq(&["WITHOUT", "UNIQUE"]) {
34308                    self.match_identifier("KEYS");
34309                    Some(Box::new(Expression::Boolean(BooleanLiteral {
34310                        value: false,
34311                    })))
34312                } else {
34313                    None
34314                };
34315                // Parse RETURNING type
34316                let return_type = if self.match_token(TokenType::Returning) {
34317                    let dt = self.parse_data_type()?;
34318                    Some(Box::new(Expression::DataType(dt)))
34319                } else {
34320                    None
34321                };
34322                self.expect(TokenType::RParen)?;
34323                Ok(Expression::JSONObjectAgg(Box::new(JSONObjectAgg {
34324                    expressions: vec![Expression::JSONKeyValue(Box::new(JSONKeyValue {
34325                        this: Box::new(key),
34326                        expression: Box::new(value_with_format),
34327                    }))],
34328                    null_handling,
34329                    unique_keys,
34330                    return_type,
34331                    encoding: None,
34332                })))
34333            }
34334
34335            // JSON_TABLE function - MySQL/Oracle table function for JSON data
34336            // JSON_TABLE(json_doc [FORMAT JSON], path COLUMNS (column_list)) [AS alias]
34337            "JSON_TABLE" => {
34338                // Parse the JSON expression
34339                let this = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
34340                // Check for FORMAT JSON after the expression
34341                let this_with_format = if self.match_text_seq(&["FORMAT", "JSON"]) {
34342                    Expression::JSONFormat(Box::new(JSONFormat {
34343                        this: Some(Box::new(this)),
34344                        options: Vec::new(),
34345                        is_json: None,
34346                        to_json: None,
34347                    }))
34348                } else {
34349                    this
34350                };
34351
34352                // Parse path (after comma)
34353                let path = if self.match_token(TokenType::Comma) {
34354                    if let Some(s) = self.parse_string()? {
34355                        Some(Box::new(s))
34356                    } else {
34357                        None
34358                    }
34359                } else {
34360                    None
34361                };
34362
34363                // Oracle uses "ERROR ON ERROR" (value then behavior) instead of "ON ERROR ERROR"
34364                // Parse error handling: ERROR ON ERROR or NULL ON ERROR
34365                let error_handling =
34366                    if self.match_identifier("ERROR") && self.match_text_seq(&["ON", "ERROR"]) {
34367                        Some(Box::new(Expression::Var(Box::new(Var {
34368                            this: "ERROR ON ERROR".to_string(),
34369                        }))))
34370                    } else if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
34371                        Some(Box::new(Expression::Var(Box::new(Var {
34372                            this: "NULL ON ERROR".to_string(),
34373                        }))))
34374                    } else {
34375                        None
34376                    };
34377
34378                // Parse empty handling: ERROR ON EMPTY or NULL ON EMPTY
34379                let empty_handling =
34380                    if self.match_identifier("ERROR") && self.match_text_seq(&["ON", "EMPTY"]) {
34381                        Some(Box::new(Expression::Var(Box::new(Var {
34382                            this: "ERROR ON EMPTY".to_string(),
34383                        }))))
34384                    } else if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
34385                        Some(Box::new(Expression::Var(Box::new(Var {
34386                            this: "NULL ON EMPTY".to_string(),
34387                        }))))
34388                    } else {
34389                        None
34390                    };
34391
34392                // Parse COLUMNS clause
34393                let schema = self.parse_json_table_columns()?;
34394
34395                self.expect(TokenType::RParen)?;
34396
34397                Ok(Expression::JSONTable(Box::new(JSONTable {
34398                    this: Box::new(this_with_format),
34399                    schema: schema.map(Box::new),
34400                    path,
34401                    error_handling,
34402                    empty_handling,
34403                })))
34404            }
34405            _ => unreachable!(
34406                "phase-6 json parser called with non-json family name '{}'",
34407                canonical_upper_name
34408            ),
34409        }
34410    }
34411
34412    fn parse_typed_translate_teradata_family(
34413        &mut self,
34414        name: &str,
34415        _upper_name: &str,
34416        canonical_upper_name: &str,
34417    ) -> Result<Expression> {
34418        match canonical_upper_name {
34419            // Teradata: TRANSLATE(x USING charset [WITH ERROR])
34420            "TRANSLATE"
34421                if matches!(
34422                    self.config.dialect,
34423                    Some(crate::dialects::DialectType::Teradata)
34424                ) =>
34425            {
34426                let this = self.parse_expression()?;
34427                if self.match_token(TokenType::Using) {
34428                    let expression = self.parse_expression()?;
34429                    let with_error = if self.match_text_seq(&["WITH", "ERROR"]) {
34430                        Some(Box::new(Expression::Boolean(BooleanLiteral {
34431                            value: true,
34432                        })))
34433                    } else {
34434                        None
34435                    };
34436                    self.expect(TokenType::RParen)?;
34437                    Ok(Expression::TranslateCharacters(Box::new(
34438                        TranslateCharacters {
34439                            this: Box::new(this),
34440                            expression: Box::new(expression),
34441                            with_error,
34442                        },
34443                    )))
34444                } else {
34445                    let mut args = vec![this];
34446                    if self.match_token(TokenType::Comma) {
34447                        let mut rest = self.parse_expression_list()?;
34448                        args.append(&mut rest);
34449                    }
34450                    self.expect(TokenType::RParen)?;
34451                    Ok(Expression::Function(Box::new(Function {
34452                        name: name.to_string(),
34453                        args,
34454                        distinct: false,
34455                        trailing_comments: Vec::new(),
34456                        use_bracket_syntax: false,
34457                        no_parens: false,
34458                        quoted: false,
34459                        span: None,
34460                        inferred_type: None,
34461                    })))
34462                }
34463            }
34464
34465            _ => unreachable!(
34466                "phase-6 translate parser called with non-translate family name '{}'",
34467                canonical_upper_name
34468            ),
34469        }
34470    }
34471
34472    /// Parse a generic function call (fallback for unrecognized functions)
34473    fn parse_generic_function(&mut self, name: &str, quoted: bool) -> Result<Expression> {
34474        let is_known_agg = Self::is_aggregate_function(name);
34475
34476        let (args, distinct) = if self.check(TokenType::RParen) {
34477            (Vec::new(), false)
34478        } else if self.check(TokenType::Star) {
34479            // Check for DuckDB *COLUMNS(...) syntax first
34480            if self.check_next_identifier("COLUMNS")
34481                && self
34482                    .tokens
34483                    .get(self.current + 2)
34484                    .map(|t| t.token_type == TokenType::LParen)
34485                    .unwrap_or(false)
34486            {
34487                // Parse *COLUMNS(...) as a function argument
34488                (self.parse_function_arguments()?, false)
34489            } else {
34490                // Regular star: parse star modifiers like EXCLUDE/EXCEPT/REPLACE/RENAME
34491                // e.g., COLUMNS(* EXCLUDE (empid, dept))
34492                self.skip(); // consume *
34493                let star = self.parse_star_modifiers(None)?;
34494                let mut args = vec![Expression::Star(star)];
34495                // ClickHouse: func(*, col1, col2) — star followed by more args
34496                if self.match_token(TokenType::Comma) {
34497                    let rest = self.parse_function_arguments()?;
34498                    args.extend(rest);
34499                }
34500                (args, false)
34501            }
34502        } else if self.check(TokenType::Distinct)
34503            && !self.check_next(TokenType::Comma)
34504            && !self.check_next(TokenType::RParen)
34505        {
34506            // DISTINCT as aggregate modifier: func(DISTINCT expr)
34507            // Not when followed by comma or rparen — then DISTINCT is used as an identifier value
34508            self.skip(); // consume DISTINCT
34509            (self.parse_function_arguments()?, true)
34510        } else if is_known_agg && self.match_token(TokenType::All) {
34511            // ALL is the default quantifier, just consume it
34512            (self.parse_function_arguments()?, false)
34513        } else {
34514            (self.parse_function_arguments()?, false)
34515        };
34516
34517        // For known aggregate functions, check for IGNORE NULLS, ORDER BY, LIMIT inside parens
34518        let (ignore_nulls, order_by, agg_limit) = if is_known_agg {
34519            let ignore_nulls = if self.match_token(TokenType::Ignore)
34520                && self.match_token(TokenType::Nulls)
34521            {
34522                Some(true)
34523            } else if self.match_token(TokenType::Respect) && self.match_token(TokenType::Nulls) {
34524                Some(false)
34525            } else {
34526                None
34527            };
34528
34529            let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
34530                self.parse_order_by_list()?
34531            } else {
34532                Vec::new()
34533            };
34534            let limit = if self.match_token(TokenType::Limit) {
34535                Some(Box::new(self.parse_expression()?))
34536            } else {
34537                None
34538            };
34539            (ignore_nulls, order_by, limit)
34540        } else {
34541            (None, Vec::new(), None)
34542        };
34543
34544        // ClickHouse: SETTINGS key=value, ... before closing paren in function calls
34545        if matches!(
34546            self.config.dialect,
34547            Some(crate::dialects::DialectType::ClickHouse)
34548        ) && self.check(TokenType::Settings)
34549            && self.current + 2 < self.tokens.len()
34550            && (self.tokens[self.current + 1].token_type == TokenType::Var
34551                || self.tokens[self.current + 1].token_type == TokenType::Identifier)
34552            && self.tokens[self.current + 2].token_type == TokenType::Eq
34553        {
34554            self.skip(); // consume SETTINGS
34555            loop {
34556                let _key = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34557                    self.advance().text
34558                } else {
34559                    break;
34560                };
34561                if self.match_token(TokenType::Eq) {
34562                    let _value = self.parse_primary()?;
34563                }
34564                if !self.match_token(TokenType::Comma) {
34565                    break;
34566                }
34567            }
34568        }
34569
34570        self.expect(TokenType::RParen)?;
34571        let trailing_comments = self.previous_trailing_comments().to_vec();
34572
34573        // Check for WITHIN GROUP (ORDER BY ...)
34574        if self.match_identifier("WITHIN") {
34575            if self.match_identifier("GROUP") {
34576                self.expect(TokenType::LParen)?;
34577                self.expect(TokenType::Order)?;
34578                self.expect(TokenType::By)?;
34579                let within_order = self.parse_order_by_list()?;
34580                self.expect(TokenType::RParen)?;
34581
34582                let func_expr = Expression::AggregateFunction(Box::new(AggregateFunction {
34583                    name: name.to_string(),
34584                    args,
34585                    distinct,
34586                    filter: None,
34587                    order_by: Vec::new(),
34588                    limit: None,
34589                    ignore_nulls: None,
34590                    inferred_type: None,
34591                }));
34592
34593                let within = Expression::WithinGroup(Box::new(WithinGroup {
34594                    this: func_expr,
34595                    order_by: within_order,
34596                }));
34597
34598                // Check for FILTER after WITHIN GROUP
34599                let filter = self.parse_filter_clause()?;
34600                if let Some(filter_expr) = filter {
34601                    return Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
34602                        name: format!("__WITHIN_GROUP_{}", name),
34603                        args: vec![within, filter_expr],
34604                        distinct: false,
34605                        filter: None,
34606                        order_by: Vec::new(),
34607                        limit: None,
34608                        ignore_nulls: None,
34609                        inferred_type: None,
34610                    })));
34611                }
34612
34613                return Ok(within);
34614            }
34615        }
34616
34617        let filter = self.parse_filter_clause()?;
34618
34619        // Check for postfix IGNORE NULLS / RESPECT NULLS after RParen
34620        let ignore_nulls = if ignore_nulls.is_some() {
34621            ignore_nulls
34622        } else if self.match_keywords(&[TokenType::Ignore, TokenType::Nulls]) {
34623            Some(true)
34624        } else if self.match_keywords(&[TokenType::Respect, TokenType::Nulls]) {
34625            Some(false)
34626        } else {
34627            None
34628        };
34629
34630        if filter.is_some() || is_known_agg || ignore_nulls.is_some() {
34631            Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
34632                name: name.to_string(),
34633                args,
34634                distinct,
34635                filter,
34636                order_by,
34637                limit: agg_limit,
34638                ignore_nulls,
34639                inferred_type: None,
34640            })))
34641        } else {
34642            let mut func = Function::new(name.to_string(), args);
34643            func.distinct = distinct;
34644            func.trailing_comments = trailing_comments;
34645            func.quoted = quoted;
34646            Ok(Expression::Function(Box::new(func)))
34647        }
34648    }
34649
34650    /// Check for an AS alias after an expression in ClickHouse function arg context.
34651    fn maybe_clickhouse_alias(&mut self, expr: Expression) -> Expression {
34652        if matches!(
34653            self.config.dialect,
34654            Some(crate::dialects::DialectType::ClickHouse)
34655        ) && self.check(TokenType::As)
34656            && !self.check_next(TokenType::RParen)
34657            && !self.check_next(TokenType::Comma)
34658        {
34659            let next_idx = self.current + 1;
34660            let is_alias = next_idx < self.tokens.len()
34661                && matches!(
34662                    self.tokens[next_idx].token_type,
34663                    TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
34664                );
34665            if is_alias {
34666                self.skip(); // consume AS
34667                let alias_token = self.advance();
34668                let alias_name = Identifier {
34669                    name: alias_token.text.clone(),
34670                    quoted: alias_token.token_type == TokenType::QuotedIdentifier,
34671                    trailing_comments: Vec::new(),
34672                    span: None,
34673                };
34674                return Expression::Alias(Box::new(crate::expressions::Alias {
34675                    this: expr,
34676                    alias: alias_name,
34677                    column_aliases: Vec::new(),
34678                    pre_alias_comments: Vec::new(),
34679                    trailing_comments: Vec::new(),
34680                    inferred_type: None,
34681                }));
34682            }
34683        }
34684        expr
34685    }
34686
34687    /// Parse an expression, then check for AS alias in ClickHouse function arg context.
34688    /// ClickHouse allows: func(expr AS alias, ...) where AS creates a named alias inside function args.
34689    fn parse_expression_with_clickhouse_alias(&mut self) -> Result<Expression> {
34690        let expr = self.parse_expression()?;
34691        Ok(self.maybe_clickhouse_alias(expr))
34692    }
34693
34694    /// Parse function arguments, handling named arguments (name => value, name := value)
34695    /// and TABLE/MODEL prefixed arguments (BigQuery)
34696    fn parse_function_arguments(&mut self) -> Result<Vec<Expression>> {
34697        let mut args = Vec::new();
34698
34699        loop {
34700            // ClickHouse: SETTINGS key=value, ... terminates function args
34701            // Only break if SETTINGS is followed by identifier = value pattern
34702            if matches!(
34703                self.config.dialect,
34704                Some(crate::dialects::DialectType::ClickHouse)
34705            ) && self.check(TokenType::Settings)
34706                && self.current + 2 < self.tokens.len()
34707                && (self.tokens[self.current + 1].token_type == TokenType::Var
34708                    || self.tokens[self.current + 1].token_type == TokenType::Identifier)
34709                && self.tokens[self.current + 2].token_type == TokenType::Eq
34710            {
34711                break; // will be consumed by SETTINGS handler after loop
34712            }
34713
34714            // ClickHouse: bare SELECT/WITH as function argument (e.g., view(SELECT 1), remote(..., view(SELECT ...)))
34715            if matches!(
34716                self.config.dialect,
34717                Some(crate::dialects::DialectType::ClickHouse)
34718            ) && (self.check(TokenType::Select) || self.check(TokenType::With))
34719            {
34720                let query = self.parse_statement()?;
34721                args.push(query);
34722                if !self.match_token(TokenType::Comma) {
34723                    break;
34724                }
34725                continue;
34726            }
34727
34728            // Check for TABLE ref or MODEL ref as function argument (BigQuery)
34729            // e.g., GAP_FILL(TABLE device_data, ...) or ML.PREDICT(MODEL mydataset.mymodel, ...)
34730            let is_table_or_model_arg = if !self.is_at_end() {
34731                self.check(TokenType::Table) || self.peek().text.eq_ignore_ascii_case("MODEL")
34732            } else {
34733                false
34734            };
34735            let arg = if is_table_or_model_arg {
34736                let prefix = self.peek().text.to_ascii_uppercase();
34737                let saved_pos = self.current;
34738                self.skip(); // consume TABLE or MODEL
34739
34740                // Only treat as TABLE/MODEL argument if followed by an identifier (table name),
34741                // not by => (which would be a named arg like "table => value")
34742                if !self.is_at_end()
34743                    && !self.check(TokenType::FArrow)
34744                    && !self.check(TokenType::ColonEq)
34745                {
34746                    // Parse the table/model reference (supports dotted names like dataset.table)
34747                    if let Some(table_expr) = self.parse_table_parts()? {
34748                        Expression::TableArgument(Box::new(TableArgument {
34749                            prefix,
34750                            this: table_expr,
34751                        }))
34752                    } else {
34753                        // Failed to parse table parts, backtrack and treat as regular expression
34754                        self.current = saved_pos;
34755                        self.parse_expression()?
34756                    }
34757                } else {
34758                    // TABLE/MODEL followed by => or :=, backtrack and handle as named arg
34759                    self.current = saved_pos;
34760                    if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34761                        let ident_token = self.advance();
34762                        let ident_name = ident_token.text.clone();
34763                        if self.match_token(TokenType::FArrow) {
34764                            let value = self.parse_expression()?;
34765                            Expression::NamedArgument(Box::new(NamedArgument {
34766                                name: Identifier::new(ident_name),
34767                                value,
34768                                separator: NamedArgSeparator::DArrow,
34769                            }))
34770                        } else if self.match_token(TokenType::ColonEq) {
34771                            let value = self.parse_expression()?;
34772                            Expression::NamedArgument(Box::new(NamedArgument {
34773                                name: Identifier::new(ident_name),
34774                                value,
34775                                separator: NamedArgSeparator::ColonEq,
34776                            }))
34777                        } else {
34778                            self.current = saved_pos;
34779                            self.parse_expression()?
34780                        }
34781                    } else {
34782                        self.parse_expression()?
34783                    }
34784                }
34785            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34786                // Try to parse:
34787                // 1. Named argument: identifier => value or identifier := value
34788                // 2. Snowflake lambda with type: identifier type -> body (e.g., a int -> a + 1)
34789                // Save position to backtrack if not a named argument
34790                let saved_pos = self.current;
34791
34792                // Try to get identifier
34793                let ident_token = self.advance();
34794                let ident_name = ident_token.text.clone();
34795
34796                // PostgreSQL/Redshift VARIADIC keyword: backtrack and let parse_expression handle it
34797                // VARIADIC ARRAY[...] must not be misinterpreted as a lambda with type annotation
34798                if ident_name.eq_ignore_ascii_case("VARIADIC")
34799                    && matches!(
34800                        self.config.dialect,
34801                        Some(crate::dialects::DialectType::PostgreSQL)
34802                            | Some(crate::dialects::DialectType::Redshift)
34803                    )
34804                {
34805                    self.current = saved_pos;
34806                    self.parse_expression()?
34807                }
34808                // Check for Snowflake lambda with type annotation: a int -> body
34809                // Look ahead to see if we have a type token followed by ->
34810                else if !self.is_at_end()
34811                    && self.is_type_keyword()
34812                    && !self.check(TokenType::FArrow)
34813                    && !self.check(TokenType::ColonEq)
34814                {
34815                    // Parse type annotation
34816                    let type_annotation = self.parse_data_type()?;
34817
34818                    // Check for arrow
34819                    if self.match_token(TokenType::Arrow) {
34820                        // This is a Snowflake lambda: param type -> body
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![Some(type_annotation)],
34827                        }))
34828                    } else {
34829                        // Not a lambda, backtrack and parse as regular expression
34830                        self.current = saved_pos;
34831                        self.parse_expression()?
34832                    }
34833                }
34834                // ClickHouse: simple lambda without type annotation: ident -> body
34835                else if self.match_token(TokenType::Arrow) {
34836                    let body = self.parse_expression()?;
34837                    Expression::Lambda(Box::new(LambdaExpr {
34838                        parameters: vec![Identifier::new(ident_name)],
34839                        body,
34840                        colon: false,
34841                        parameter_types: Vec::new(),
34842                    }))
34843                }
34844                // Check for named argument separator (=> is FArrow)
34845                else if self.match_token(TokenType::FArrow) {
34846                    // name => value
34847                    let value = self.parse_expression()?;
34848                    Expression::NamedArgument(Box::new(NamedArgument {
34849                        name: Identifier::new(ident_name),
34850                        value,
34851                        separator: NamedArgSeparator::DArrow,
34852                    }))
34853                } else if self.match_token(TokenType::ColonEq) {
34854                    // name := value
34855                    let value = self.parse_expression()?;
34856                    Expression::NamedArgument(Box::new(NamedArgument {
34857                        name: Identifier::new(ident_name),
34858                        value,
34859                        separator: NamedArgSeparator::ColonEq,
34860                    }))
34861                } else {
34862                    // Not a named argument, backtrack and parse as regular expression
34863                    self.current = saved_pos;
34864                    self.parse_expression()?
34865                }
34866            } else {
34867                // Regular expression
34868                self.parse_expression()?
34869            };
34870
34871            // Handle AS alias inside function arguments (e.g. ClickHouse: arrayJoin([1,2,3] AS src))
34872            let arg = if matches!(
34873                self.config.dialect,
34874                Some(crate::dialects::DialectType::ClickHouse)
34875            ) && self.check(TokenType::As)
34876                && !self.check_next(TokenType::RParen)
34877                && !self.check_next(TokenType::Comma)
34878            {
34879                // Look ahead: AS followed by identifier/keyword, then ) or , means it's an alias
34880                let next_idx = self.current + 1;
34881                let after_alias_idx = self.current + 2;
34882                let is_alias_token = next_idx < self.tokens.len()
34883                    && (matches!(
34884                        self.tokens[next_idx].token_type,
34885                        TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
34886                    ) || self.tokens[next_idx].token_type.is_keyword());
34887                // Ensure the token AFTER the alias is ) or , (function arg boundary)
34888                let is_alias = is_alias_token
34889                    && after_alias_idx < self.tokens.len()
34890                    && matches!(
34891                        self.tokens[after_alias_idx].token_type,
34892                        TokenType::RParen | TokenType::Comma
34893                    );
34894                if is_alias {
34895                    self.skip(); // consume AS
34896                    let alias_token = self.advance();
34897                    let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
34898                        let mut ident = Identifier::new(alias_token.text.clone());
34899                        ident.quoted = true;
34900                        ident
34901                    } else {
34902                        Identifier::new(alias_token.text.clone())
34903                    };
34904                    Expression::Alias(Box::new(crate::expressions::Alias {
34905                        this: arg,
34906                        alias: alias_name,
34907                        column_aliases: Vec::new(),
34908                        pre_alias_comments: Vec::new(),
34909                        trailing_comments: Vec::new(),
34910                        inferred_type: None,
34911                    }))
34912                } else {
34913                    arg
34914                }
34915            } else {
34916                arg
34917            };
34918
34919            // ClickHouse: implicit alias without AS keyword: func(expr identifier, ...)
34920            let arg = self.try_clickhouse_implicit_alias(arg);
34921
34922            // Handle trailing comments
34923            let trailing_comments = self.previous_trailing_comments().to_vec();
34924            let arg = if trailing_comments.is_empty() {
34925                arg
34926            } else {
34927                match &arg {
34928                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
34929                        Expression::Annotated(Box::new(Annotated {
34930                            this: arg,
34931                            trailing_comments,
34932                        }))
34933                    }
34934                    _ => arg,
34935                }
34936            };
34937
34938            args.push(arg);
34939
34940            if !self.match_token(TokenType::Comma) {
34941                break;
34942            }
34943            // Skip consecutive commas (Snowflake allows skipping optional named args)
34944            // e.g., ROUND(SCALE => 1, EXPR => 2.25, , ROUNDING_MODE => 'HALF_TO_EVEN')
34945            while self.check(TokenType::Comma) {
34946                self.skip();
34947            }
34948        }
34949
34950        // ClickHouse: SETTINGS key=value, ... at end of function args before RParen
34951        if matches!(
34952            self.config.dialect,
34953            Some(crate::dialects::DialectType::ClickHouse)
34954        ) && self.check(TokenType::Settings)
34955            && self.current + 2 < self.tokens.len()
34956            && (self.tokens[self.current + 1].token_type == TokenType::Var
34957                || self.tokens[self.current + 1].token_type == TokenType::Identifier)
34958            && self.tokens[self.current + 2].token_type == TokenType::Eq
34959        {
34960            self.skip(); // consume SETTINGS
34961            loop {
34962                let _key = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
34963                    self.advance().text
34964                } else {
34965                    break;
34966                };
34967                if self.match_token(TokenType::Eq) {
34968                    let _value = self.parse_primary()?;
34969                }
34970                if !self.match_token(TokenType::Comma) {
34971                    break;
34972                }
34973            }
34974        }
34975
34976        Ok(args)
34977    }
34978
34979    /// Parse optional FILTER clause
34980    fn parse_filter_clause(&mut self) -> Result<Option<Expression>> {
34981        if self.match_token(TokenType::Filter) {
34982            self.expect(TokenType::LParen)?;
34983            // WHERE is optional (DuckDB allows FILTER(condition) without WHERE)
34984            self.match_token(TokenType::Where);
34985            let filter_expr = self.parse_expression()?;
34986            self.expect(TokenType::RParen)?;
34987            Ok(Some(filter_expr))
34988        } else {
34989            Ok(None)
34990        }
34991    }
34992
34993    /// Parse STRUCT arguments with optional AS aliases: STRUCT(x, y AS name, ...)
34994    fn parse_struct_args(&mut self) -> Result<Vec<Expression>> {
34995        let mut args = Vec::new();
34996
34997        loop {
34998            let expr = self.parse_expression()?;
34999
35000            // Check for AS alias
35001            if self.match_token(TokenType::As) {
35002                let alias = self.expect_identifier_or_keyword()?;
35003                args.push(Expression::Alias(Box::new(Alias {
35004                    this: expr,
35005                    alias: Identifier::new(alias),
35006                    column_aliases: Vec::new(),
35007                    pre_alias_comments: Vec::new(),
35008                    trailing_comments: Vec::new(),
35009                    inferred_type: None,
35010                })));
35011            } else {
35012                args.push(expr);
35013            }
35014
35015            if !self.match_token(TokenType::Comma) {
35016                break;
35017            }
35018        }
35019
35020        Ok(args)
35021    }
35022
35023    /// Maybe parse OVER clause for window functions or WITHIN GROUP for ordered-set aggregates
35024    fn maybe_parse_over(&mut self, expr: Expression) -> Result<Expression> {
35025        let expr = self.maybe_parse_subscript(expr)?;
35026
35027        // For Oracle: Check for interval span after expression (e.g., (expr) DAY(9) TO SECOND(3))
35028        // https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Interval-Expressions.html
35029        let expr = if matches!(
35030            self.config.dialect,
35031            Some(crate::dialects::DialectType::Oracle)
35032        ) {
35033            self.try_parse_oracle_interval_span(expr)?
35034        } else {
35035            expr
35036        };
35037
35038        // Check for WITHIN GROUP (for ordered-set aggregate functions like LISTAGG, PERCENTILE_CONT)
35039        let expr = if self.check(TokenType::Within) && self.check_next(TokenType::Group) {
35040            self.skip(); // consume WITHIN
35041            self.skip(); // consume GROUP
35042            self.expect(TokenType::LParen)?;
35043            self.expect(TokenType::Order)?;
35044            self.expect(TokenType::By)?;
35045            let order_by = self.parse_order_by_list()?;
35046            self.expect(TokenType::RParen)?;
35047            Expression::WithinGroup(Box::new(WithinGroup {
35048                this: expr,
35049                order_by,
35050            }))
35051        } else {
35052            expr
35053        };
35054
35055        // Check for FILTER clause (can follow WITHIN GROUP or standalone aggregate)
35056        // SQL:2003 syntax: aggregate_function(...) FILTER (WHERE condition)
35057        let expr = if self.match_token(TokenType::Filter) {
35058            self.expect(TokenType::LParen)?;
35059            // WHERE is required in standard SQL FILTER clause
35060            self.expect(TokenType::Where)?;
35061            let filter_expr = self.parse_expression()?;
35062            self.expect(TokenType::RParen)?;
35063            Expression::Filter(Box::new(Filter {
35064                this: Box::new(expr),
35065                expression: Box::new(filter_expr),
35066            }))
35067        } else {
35068            expr
35069        };
35070
35071        // ClickHouse: IGNORE NULLS / RESPECT NULLS modifier after function call (before OVER)
35072        // This handles cases like: func(args) IGNORE NULLS OVER w
35073        // and parametric aggregates: func(params)(args) IGNORE NULLS
35074        let expr = if matches!(
35075            self.config.dialect,
35076            Some(crate::dialects::DialectType::ClickHouse)
35077        ) && (self.match_keywords(&[TokenType::Ignore, TokenType::Nulls])
35078            || self.match_keywords(&[TokenType::Respect, TokenType::Nulls]))
35079        {
35080            // Consume the modifier — we don't need to store it for transpilation
35081            expr
35082        } else {
35083            expr
35084        };
35085
35086        // Check for KEEP clause (Oracle: aggregate KEEP (DENSE_RANK FIRST|LAST ORDER BY ...))
35087        // Only if KEEP is followed by LPAREN - otherwise KEEP is used as an alias
35088        let keep = if self.check(TokenType::Keep) && self.check_next(TokenType::LParen) {
35089            self.skip(); // consume KEEP
35090            Some(self.parse_keep_clause()?)
35091        } else {
35092            None
35093        };
35094
35095        // Check for OVER clause (can follow KEEP, FILTER, WITHIN GROUP, or standalone aggregate)
35096        if self.match_token(TokenType::Over) {
35097            let over = self.parse_over_clause()?;
35098            Ok(Expression::WindowFunction(Box::new(WindowFunction {
35099                this: expr,
35100                over,
35101                keep,
35102                inferred_type: None,
35103            })))
35104        } else if keep.is_some() {
35105            // KEEP without OVER - still a window-like construct
35106            // Create a WindowFunction with empty Over
35107            Ok(Expression::WindowFunction(Box::new(WindowFunction {
35108                this: expr,
35109                over: Over {
35110                    window_name: None,
35111                    partition_by: Vec::new(),
35112                    order_by: Vec::new(),
35113                    frame: None,
35114                    alias: None,
35115                },
35116                keep,
35117                inferred_type: None,
35118            })))
35119        } else {
35120            Ok(expr)
35121        }
35122    }
35123
35124    /// ClickHouse: parse parameterized aggregate functions like func(params)(args)
35125    fn maybe_parse_clickhouse_parameterized_agg(&mut self, expr: Expression) -> Result<Expression> {
35126        if !matches!(
35127            self.config.dialect,
35128            Some(crate::dialects::DialectType::ClickHouse)
35129        ) {
35130            return Ok(expr);
35131        }
35132        if !self.check(TokenType::LParen) {
35133            return Ok(expr);
35134        }
35135
35136        let (name, quoted, params) = match expr {
35137            Expression::Function(func) => (func.name, func.quoted, func.args),
35138            Expression::AggregateFunction(agg) => {
35139                if agg.distinct
35140                    || agg.filter.is_some()
35141                    || !agg.order_by.is_empty()
35142                    || agg.limit.is_some()
35143                    || agg.ignore_nulls.is_some()
35144                {
35145                    return Ok(Expression::AggregateFunction(agg));
35146                }
35147                (agg.name, false, agg.args)
35148            }
35149            _ => return Ok(expr),
35150        };
35151
35152        self.skip(); // consume (
35153                     // Handle DISTINCT in second arg list: func(params)(DISTINCT args)
35154        let distinct = self.match_token(TokenType::Distinct);
35155        let expressions = if self.check(TokenType::RParen) {
35156            Vec::new()
35157        } else {
35158            self.parse_function_arguments()?
35159        };
35160        self.expect(TokenType::RParen)?;
35161
35162        let ident = Identifier {
35163            name,
35164            quoted,
35165            trailing_comments: Vec::new(),
35166            span: None,
35167        };
35168
35169        // If DISTINCT was used, wrap the result to indicate it
35170        // For now, we just include it in the CombinedParameterizedAgg
35171        let _ = distinct; // DISTINCT is consumed but not separately tracked in this AST node
35172        Ok(Expression::CombinedParameterizedAgg(Box::new(
35173            CombinedParameterizedAgg {
35174                this: Box::new(Expression::Identifier(ident)),
35175                params,
35176                expressions,
35177            },
35178        )))
35179    }
35180
35181    /// Parse Oracle KEEP clause: KEEP (DENSE_RANK FIRST|LAST ORDER BY ...)
35182    fn parse_keep_clause(&mut self) -> Result<Keep> {
35183        self.expect(TokenType::LParen)?;
35184
35185        // Expect DENSE_RANK
35186        if !self.match_identifier("DENSE_RANK") {
35187            return Err(self.parse_error("Expected DENSE_RANK in KEEP clause"));
35188        }
35189
35190        // Expect FIRST or LAST
35191        let first = if self.match_token(TokenType::First) {
35192            true
35193        } else if self.match_token(TokenType::Last) {
35194            false
35195        } else {
35196            return Err(self.parse_error("Expected FIRST or LAST in KEEP clause"));
35197        };
35198
35199        // Expect ORDER BY
35200        self.expect(TokenType::Order)?;
35201        self.expect(TokenType::By)?;
35202
35203        let order_by = self.parse_order_by_list()?;
35204
35205        self.expect(TokenType::RParen)?;
35206
35207        Ok(Keep { first, order_by })
35208    }
35209
35210    /// Parse a JSON path operand - just the immediate literal/identifier without any subscript processing
35211    /// This is used for JSON arrow operators (->, ->>) to get proper left-to-right associativity
35212    fn parse_json_path_operand(&mut self) -> Result<Expression> {
35213        // Negative number literal (e.g., -1)
35214        if self.check(TokenType::Dash) {
35215            let dash_pos = self.current;
35216            self.skip(); // consume the dash
35217            if self.check(TokenType::Number) {
35218                let token = self.advance();
35219                return Ok(Expression::Neg(Box::new(UnaryOp {
35220                    this: Expression::Literal(Box::new(Literal::Number(token.text))),
35221                    inferred_type: None,
35222                })));
35223            }
35224            // Not a negative number, backtrack
35225            self.current = dash_pos;
35226        }
35227
35228        // Number literal
35229        if self.check(TokenType::Number) {
35230            let token = self.advance();
35231            // Check for numeric literal suffix encoded as "number::TYPE" by tokenizer
35232            if let Some(sep_pos) = token.text.find("::") {
35233                let num_part = &token.text[..sep_pos];
35234                let type_name = &token.text[sep_pos + 2..];
35235                let num_expr = Expression::Literal(Box::new(Literal::Number(num_part.to_string())));
35236                let data_type = match type_name {
35237                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
35238                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
35239                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
35240                    "DOUBLE" => crate::expressions::DataType::Double {
35241                        precision: None,
35242                        scale: None,
35243                    },
35244                    "FLOAT" => crate::expressions::DataType::Float {
35245                        precision: None,
35246                        scale: None,
35247                        real_spelling: false,
35248                    },
35249                    "DECIMAL" => crate::expressions::DataType::Decimal {
35250                        precision: None,
35251                        scale: None,
35252                    },
35253                    _ => crate::expressions::DataType::Custom {
35254                        name: type_name.to_string(),
35255                    },
35256                };
35257                return Ok(Expression::TryCast(Box::new(crate::expressions::Cast {
35258                    this: num_expr,
35259                    to: data_type,
35260                    trailing_comments: Vec::new(),
35261                    double_colon_syntax: false,
35262                    format: None,
35263                    default: None,
35264                    inferred_type: None,
35265                })));
35266            }
35267            return Ok(Expression::Literal(Box::new(Literal::Number(token.text))));
35268        }
35269
35270        // String literal
35271        if self.check(TokenType::String) {
35272            let token = self.advance();
35273            return Ok(Expression::Literal(Box::new(Literal::String(token.text))));
35274        }
35275
35276        // Parenthesized expression (for complex paths)
35277        if self.match_token(TokenType::LParen) {
35278            let expr = self.parse_expression()?;
35279            self.expect(TokenType::RParen)?;
35280            return Ok(Expression::Paren(Box::new(Paren {
35281                this: expr,
35282                trailing_comments: Vec::new(),
35283            })));
35284        }
35285
35286        // Array literal: ['$.family', '$.species']
35287        // Used in DuckDB for multi-path JSON extraction
35288        if self.match_token(TokenType::LBracket) {
35289            // Empty array: []
35290            if self.match_token(TokenType::RBracket) {
35291                return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
35292                    expressions: Vec::new(),
35293                    bracket_notation: true,
35294                    use_list_keyword: false,
35295                })));
35296            }
35297
35298            // Parse array elements
35299            let mut expressions = vec![self.parse_expression()?];
35300            while self.match_token(TokenType::Comma) {
35301                if self.check(TokenType::RBracket) {
35302                    break;
35303                }
35304                expressions.push(self.parse_expression()?);
35305            }
35306            self.expect(TokenType::RBracket)?;
35307
35308            return Ok(Expression::ArrayFunc(Box::new(ArrayConstructor {
35309                expressions,
35310                bracket_notation: true,
35311                use_list_keyword: false,
35312            })));
35313        }
35314
35315        // Identifier (possibly qualified like table.column)
35316        if self.is_identifier_token() {
35317            let first_ident = self.expect_identifier_with_quoted()?;
35318
35319            // Check for qualified name: identifier.identifier
35320            if self.match_token(TokenType::Dot) {
35321                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
35322                    let second_ident = if self.is_identifier_token() {
35323                        self.expect_identifier_with_quoted()?
35324                    } else {
35325                        let token = self.advance();
35326                        Identifier::new(token.text)
35327                    };
35328                    return Ok(Expression::boxed_column(Column {
35329                        name: second_ident,
35330                        table: Some(first_ident),
35331                        join_mark: false,
35332                        trailing_comments: Vec::new(),
35333                        span: None,
35334                        inferred_type: None,
35335                    }));
35336                }
35337            }
35338
35339            return Ok(Expression::boxed_column(Column {
35340                name: first_ident,
35341                table: None,
35342                join_mark: false,
35343                trailing_comments: Vec::new(),
35344                span: None,
35345                inferred_type: None,
35346            }));
35347        }
35348
35349        // Keywords as identifiers (possibly qualified)
35350        if self.is_safe_keyword_as_identifier() {
35351            let token = self.advance();
35352            let first_ident = Identifier::new(token.text);
35353
35354            // Check for qualified name: identifier.identifier
35355            if self.match_token(TokenType::Dot) {
35356                if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
35357                    let second_ident = if self.is_identifier_token() {
35358                        self.expect_identifier_with_quoted()?
35359                    } else {
35360                        let token = self.advance();
35361                        Identifier::new(token.text)
35362                    };
35363                    return Ok(Expression::boxed_column(Column {
35364                        name: second_ident,
35365                        table: Some(first_ident),
35366                        join_mark: false,
35367                        trailing_comments: Vec::new(),
35368                        span: None,
35369                        inferred_type: None,
35370                    }));
35371                }
35372            }
35373
35374            return Ok(Expression::boxed_column(Column {
35375                name: first_ident,
35376                table: None,
35377                join_mark: false,
35378                trailing_comments: Vec::new(),
35379                span: None,
35380                inferred_type: None,
35381            }));
35382        }
35383
35384        Err(self.parse_error(format!(
35385            "Unexpected token in JSON path: {:?}",
35386            self.peek().token_type
35387        )))
35388    }
35389
35390    /// Maybe parse subscript access (array[index], struct.field)
35391    fn maybe_parse_subscript(&mut self, mut expr: Expression) -> Result<Expression> {
35392        loop {
35393            // ClickHouse: empty brackets [] in JSON paths represent Array(JSON) type access.
35394            // json.a.b[] -> json.a.b.:"Array(JSON)"
35395            // json.a.b[][] -> json.a.b.:"Array(Array(JSON))"
35396            // Check for consecutive empty bracket pairs before normal bracket handling.
35397            if matches!(
35398                self.config.dialect,
35399                Some(crate::dialects::DialectType::ClickHouse)
35400            ) && self.check(TokenType::LBracket)
35401            {
35402                let is_empty_bracket = self
35403                    .peek_nth(1)
35404                    .map_or(false, |t| t.token_type == TokenType::RBracket);
35405                if is_empty_bracket {
35406                    let mut bracket_json_type: Option<DataType> = None;
35407                    while self.check(TokenType::LBracket) {
35408                        let is_empty = self
35409                            .peek_nth(1)
35410                            .map_or(false, |t| t.token_type == TokenType::RBracket);
35411                        if is_empty {
35412                            self.skip(); // consume [
35413                            self.skip(); // consume ]
35414                            bracket_json_type = Some(DataType::Array {
35415                                element_type: Box::new(bracket_json_type.unwrap_or(DataType::Json)),
35416                                dimension: None,
35417                            });
35418                        } else {
35419                            break;
35420                        }
35421                    }
35422                    if let Some(json_type) = bracket_json_type {
35423                        expr = Expression::JSONCast(Box::new(crate::expressions::JSONCast {
35424                            this: Box::new(expr),
35425                            to: json_type,
35426                        }));
35427                        continue;
35428                    }
35429                }
35430            }
35431
35432            if self.match_token(TokenType::LBracket) {
35433                // Check if expr is an array/list constructor keyword (ARRAY[...] or LIST[...])
35434                let array_constructor_type = match &expr {
35435                    Expression::Column(col) if col.table.is_none() => {
35436                        let upper = col.name.name.to_ascii_uppercase();
35437                        if upper == "ARRAY" || upper == "LIST" {
35438                            Some(upper)
35439                        } else {
35440                            None
35441                        }
35442                    }
35443                    Expression::Identifier(id) => {
35444                        let upper = id.name.to_ascii_uppercase();
35445                        if upper == "ARRAY" || upper == "LIST" {
35446                            Some(upper)
35447                        } else {
35448                            None
35449                        }
35450                    }
35451                    _ => None,
35452                };
35453
35454                if let Some(constructor_type) = array_constructor_type {
35455                    // Parse ARRAY[expr, expr, ...] or LIST[expr, expr, ...]
35456                    // bracket_notation=false means we have the ARRAY/LIST keyword prefix
35457                    let use_list_keyword = constructor_type == "LIST";
35458                    if self.check(TokenType::RBracket) {
35459                        // Empty array: ARRAY[]
35460                        self.skip();
35461                        expr = Expression::ArrayFunc(Box::new(ArrayConstructor {
35462                            expressions: Vec::new(),
35463                            bracket_notation: false, // Has ARRAY/LIST keyword
35464                            use_list_keyword,
35465                        }));
35466                    } else {
35467                        let expressions = self.parse_expression_list()?;
35468                        self.expect(TokenType::RBracket)?;
35469                        expr = Expression::ArrayFunc(Box::new(ArrayConstructor {
35470                            expressions,
35471                            bracket_notation: false, // Has ARRAY/LIST keyword
35472                            use_list_keyword,
35473                        }));
35474                    }
35475                    continue;
35476                }
35477
35478                // Special case: MAP[...] constructor syntax
35479                // Check if expr is a MAP identifier
35480                // ClickHouse: map[key] is always subscript access, not a MAP constructor
35481                let is_map_constructor = !matches!(
35482                    self.config.dialect,
35483                    Some(crate::dialects::DialectType::ClickHouse)
35484                ) && match &expr {
35485                    Expression::Column(col) => {
35486                        col.name.name.eq_ignore_ascii_case("MAP") && col.table.is_none()
35487                    }
35488                    Expression::Identifier(id) => id.name.eq_ignore_ascii_case("MAP"),
35489                    _ => false,
35490                };
35491
35492                if is_map_constructor {
35493                    let is_materialize = matches!(
35494                        self.config.dialect,
35495                        Some(crate::dialects::DialectType::Materialize)
35496                    );
35497
35498                    // Materialize: MAP[] empty map or MAP['a' => 1, ...] with fat arrow
35499                    if is_materialize {
35500                        if self.check(TokenType::RBracket) {
35501                            // Empty map: MAP[]
35502                            self.skip();
35503                            expr = Expression::ToMap(Box::new(ToMap {
35504                                this: Box::new(Expression::Struct(Box::new(Struct {
35505                                    fields: Vec::new(),
35506                                }))),
35507                            }));
35508                            continue;
35509                        }
35510
35511                        // Parse MAP['a' => 1, 'b' => 2, ...] with fat arrow entries
35512                        // Store entries as PropertyEQ expressions (key => value)
35513                        let mut entries = Vec::new();
35514                        loop {
35515                            let key = self.parse_expression()?;
35516                            self.expect(TokenType::FArrow)?;
35517                            let value = self.parse_expression()?;
35518                            // Store as PropertyEQ which will be output as key => value
35519                            entries.push((
35520                                None,
35521                                Expression::PropertyEQ(Box::new(BinaryOp::new(key, value))),
35522                            ));
35523
35524                            if !self.match_token(TokenType::Comma) {
35525                                break;
35526                            }
35527                        }
35528                        self.expect(TokenType::RBracket)?;
35529
35530                        expr = Expression::ToMap(Box::new(ToMap {
35531                            this: Box::new(Expression::Struct(Box::new(Struct {
35532                                fields: entries,
35533                            }))),
35534                        }));
35535                        continue;
35536                    }
35537
35538                    // DuckDB/BigQuery: MAP[keys, values] syntax
35539                    let keys = self.parse_expression()?;
35540                    self.expect(TokenType::Comma)?;
35541                    let values = self.parse_expression()?;
35542                    self.expect(TokenType::RBracket)?;
35543                    expr = Expression::Function(Box::new(Function {
35544                        name: "MAP".to_string(),
35545                        args: vec![keys, values],
35546                        distinct: false,
35547                        trailing_comments: Vec::new(),
35548                        use_bracket_syntax: true,
35549                        no_parens: false,
35550                        quoted: false,
35551                        span: None,
35552                        inferred_type: None,
35553                    }));
35554                    continue;
35555                }
35556
35557                // Check for slice syntax: [start:end:step]
35558                // Handle [:...] case where start is omitted
35559                if self.check(TokenType::Colon) {
35560                    self.skip(); // consume first :
35561                                 // Parse end - use parse_slice_element to avoid : being interpreted as parameter
35562                    let end = self.parse_slice_element()?;
35563                    // Check for step (second colon)
35564                    let step = if self.match_token(TokenType::Colon) {
35565                        self.parse_slice_element()?
35566                    } else {
35567                        None
35568                    };
35569                    self.expect(TokenType::RBracket)?;
35570                    if step.is_some() {
35571                        // Three-part slice with step: Subscript with Slice index
35572                        let slice = Expression::Slice(Box::new(Slice {
35573                            this: None, // start is omitted
35574                            expression: end.map(Box::new),
35575                            step: step.map(Box::new),
35576                        }));
35577                        expr = Expression::Subscript(Box::new(Subscript {
35578                            this: expr,
35579                            index: slice,
35580                        }));
35581                    } else {
35582                        expr = Expression::ArraySlice(Box::new(ArraySlice {
35583                            this: expr,
35584                            start: None,
35585                            end,
35586                        }));
35587                    }
35588                } else {
35589                    let start = self.parse_slice_element()?;
35590                    // Check if this is a slice
35591                    if self.match_token(TokenType::Colon) {
35592                        let end = self.parse_slice_element()?;
35593                        // Check for step (second colon)
35594                        let step = if self.match_token(TokenType::Colon) {
35595                            self.parse_slice_element()?
35596                        } else {
35597                            None
35598                        };
35599                        self.expect(TokenType::RBracket)?;
35600                        if step.is_some() {
35601                            // Three-part slice with step: Subscript with Slice index
35602                            let slice = Expression::Slice(Box::new(Slice {
35603                                this: start.map(Box::new),
35604                                expression: end.map(Box::new),
35605                                step: step.map(Box::new),
35606                            }));
35607                            expr = Expression::Subscript(Box::new(Subscript {
35608                                this: expr,
35609                                index: slice,
35610                            }));
35611                        } else {
35612                            expr = Expression::ArraySlice(Box::new(ArraySlice {
35613                                this: expr,
35614                                start,
35615                                end,
35616                            }));
35617                        }
35618                    } else {
35619                        self.expect(TokenType::RBracket)?;
35620                        // Simple subscript access - start must be Some
35621                        let index =
35622                            start.unwrap_or_else(|| Expression::Null(crate::expressions::Null));
35623                        expr = Expression::Subscript(Box::new(Subscript { this: expr, index }));
35624                    }
35625                }
35626            } else if self.match_token(TokenType::DotColon) {
35627                // In ClickHouse, the type after .: may be a quoted identifier like "Array(JSON)"
35628                // which needs to be re-parsed as a proper data type.
35629                let data_type = if matches!(
35630                    self.config.dialect,
35631                    Some(crate::dialects::DialectType::ClickHouse)
35632                ) && self.check(TokenType::QuotedIdentifier)
35633                {
35634                    let type_text = self.advance().text.clone();
35635                    // Re-parse the quoted identifier text as a data type
35636                    self.parse_data_type_from_text(&type_text)?
35637                } else {
35638                    self.parse_data_type()?
35639                };
35640                expr = Expression::JSONCast(Box::new(JSONCast {
35641                    this: Box::new(expr),
35642                    to: data_type,
35643                }));
35644            } else if self.match_token(TokenType::Dot) {
35645                // Handle chained dot access (a.b.c.d)
35646                if self.match_token(TokenType::Star) {
35647                    // expr.* - struct field expansion with potential modifiers (EXCEPT, REPLACE, etc.)
35648                    let table_name = match &expr {
35649                        Expression::Column(col) => {
35650                            if let Some(ref table) = col.table {
35651                                Some(Identifier::new(format!("{}.{}", table.name, col.name.name)))
35652                            } else {
35653                                Some(col.name.clone())
35654                            }
35655                        }
35656                        Expression::Dot(d) => {
35657                            fn dot_to_name_inner(expr: &Expression) -> String {
35658                                match expr {
35659                                    Expression::Column(col) => {
35660                                        if let Some(ref table) = col.table {
35661                                            format!("{}.{}", table.name, col.name.name)
35662                                        } else {
35663                                            col.name.name.clone()
35664                                        }
35665                                    }
35666                                    Expression::Dot(d) => {
35667                                        format!("{}.{}", dot_to_name_inner(&d.this), d.field.name)
35668                                    }
35669                                    _ => String::new(),
35670                                }
35671                            }
35672                            Some(Identifier::new(dot_to_name_inner(&Expression::Dot(
35673                                d.clone(),
35674                            ))))
35675                        }
35676                        _ => None,
35677                    };
35678                    if table_name.is_some() {
35679                        let star = self.parse_star_modifiers(table_name)?;
35680                        expr = Expression::Star(star);
35681                        // ClickHouse: a.* APPLY(func) EXCEPT(col) REPLACE(expr AS col) in any order
35682                        if matches!(
35683                            self.config.dialect,
35684                            Some(crate::dialects::DialectType::ClickHouse)
35685                        ) {
35686                            loop {
35687                                if self.check(TokenType::Apply) {
35688                                    self.skip();
35689                                    let apply_expr = if self.match_token(TokenType::LParen) {
35690                                        let e = self.parse_expression()?;
35691                                        self.expect(TokenType::RParen)?;
35692                                        e
35693                                    } else {
35694                                        self.parse_expression()?
35695                                    };
35696                                    expr = Expression::Apply(Box::new(crate::expressions::Apply {
35697                                        this: Box::new(expr),
35698                                        expression: Box::new(apply_expr),
35699                                    }));
35700                                } else if self.check(TokenType::Except)
35701                                    || self.check(TokenType::Exclude)
35702                                {
35703                                    self.skip();
35704                                    self.match_identifier("STRICT");
35705                                    if self.match_token(TokenType::LParen) {
35706                                        loop {
35707                                            if self.check(TokenType::RParen) {
35708                                                break;
35709                                            }
35710                                            let _ = self.parse_expression()?;
35711                                            if !self.match_token(TokenType::Comma) {
35712                                                break;
35713                                            }
35714                                        }
35715                                        self.expect(TokenType::RParen)?;
35716                                    } else if self.is_identifier_token()
35717                                        || self.is_safe_keyword_as_identifier()
35718                                    {
35719                                        let _ = self.parse_expression()?;
35720                                    }
35721                                } else if self.check(TokenType::Replace) {
35722                                    self.skip();
35723                                    self.match_identifier("STRICT");
35724                                    if self.match_token(TokenType::LParen) {
35725                                        loop {
35726                                            if self.check(TokenType::RParen) {
35727                                                break;
35728                                            }
35729                                            let _ = self.parse_expression()?;
35730                                            if self.match_token(TokenType::As) {
35731                                                if self.is_identifier_token()
35732                                                    || self.is_safe_keyword_as_identifier()
35733                                                {
35734                                                    self.skip();
35735                                                }
35736                                            }
35737                                            if !self.match_token(TokenType::Comma) {
35738                                                break;
35739                                            }
35740                                        }
35741                                        self.expect(TokenType::RParen)?;
35742                                    } else {
35743                                        let _ = self.parse_expression()?;
35744                                        if self.match_token(TokenType::As) {
35745                                            if self.is_identifier_token()
35746                                                || self.is_safe_keyword_as_identifier()
35747                                            {
35748                                                self.skip();
35749                                            }
35750                                        }
35751                                    }
35752                                } else {
35753                                    break;
35754                                }
35755                            }
35756                        }
35757                    } else {
35758                        // For complex expressions (like CAST, function calls), use Dot with * as field
35759                        expr = Expression::Dot(Box::new(DotAccess {
35760                            this: expr,
35761                            field: Identifier::new("*"),
35762                        }));
35763                    }
35764                } else if self.check(TokenType::Identifier)
35765                    || self.check(TokenType::Var)
35766                    || self.check(TokenType::QuotedIdentifier)
35767                    || self.check_keyword()
35768                {
35769                    let is_quoted = self.check(TokenType::QuotedIdentifier);
35770                    let field_name = self.advance().text;
35771                    // Check if this is a method call (field followed by parentheses)
35772                    if self.check(TokenType::LParen) && !is_quoted {
35773                        // This is a method call like a.b.C() or x.EXTRACT()
35774                        self.skip(); // consume (
35775                        let args = if self.check(TokenType::RParen) {
35776                            Vec::new()
35777                        } else {
35778                            self.parse_expression_list()?
35779                        };
35780                        self.expect(TokenType::RParen)?;
35781                        // Create a method call expression (DotAccess with function call)
35782                        expr = Expression::MethodCall(Box::new(MethodCall {
35783                            this: expr,
35784                            method: Identifier::new(field_name),
35785                            args,
35786                        }));
35787                    } else {
35788                        let mut ident = Identifier::new(field_name);
35789                        if is_quoted {
35790                            ident.quoted = true;
35791                        }
35792                        expr = Expression::Dot(Box::new(DotAccess {
35793                            this: expr,
35794                            field: ident,
35795                        }));
35796                    }
35797                } else if self.check(TokenType::Number) {
35798                    // Handle numeric field access like a.0 or x.1
35799                    let field_name = self.advance().text;
35800                    expr = Expression::Dot(Box::new(DotAccess {
35801                        this: expr,
35802                        field: Identifier::new(field_name),
35803                    }));
35804                } else if matches!(
35805                    self.config.dialect,
35806                    Some(crate::dialects::DialectType::ClickHouse)
35807                ) && self.check(TokenType::Caret)
35808                {
35809                    // ClickHouse: json.^path — the ^ prefix means "get all nested subcolumns"
35810                    self.skip(); // consume ^
35811                                 // What follows should be an identifier path
35812                    let mut field_name = "^".to_string();
35813                    if self.check(TokenType::Identifier)
35814                        || self.check(TokenType::Var)
35815                        || self.check_keyword()
35816                    {
35817                        field_name.push_str(&self.advance().text);
35818                    }
35819                    expr = Expression::Dot(Box::new(DotAccess {
35820                        this: expr,
35821                        field: Identifier::new(field_name),
35822                    }));
35823                } else if matches!(
35824                    self.config.dialect,
35825                    Some(crate::dialects::DialectType::ClickHouse)
35826                ) && self.check(TokenType::Colon)
35827                {
35828                    // ClickHouse: json.path.:Type — the : prefix means type cast on JSON path
35829                    self.skip(); // consume :
35830                                 // Consume the type name
35831                    let mut type_name = ":".to_string();
35832                    if self.check(TokenType::Identifier)
35833                        || self.check(TokenType::Var)
35834                        || self.check_keyword()
35835                    {
35836                        type_name.push_str(&self.advance().text);
35837                    }
35838                    expr = Expression::Dot(Box::new(DotAccess {
35839                        this: expr,
35840                        field: Identifier::new(type_name),
35841                    }));
35842                } else if matches!(
35843                    self.config.dialect,
35844                    Some(crate::dialects::DialectType::ClickHouse)
35845                ) && self.check(TokenType::Dash)
35846                    && self
35847                        .peek_nth(1)
35848                        .is_some_and(|t| t.token_type == TokenType::Number)
35849                {
35850                    // ClickHouse: tuple.-1 — negative tuple index
35851                    self.skip(); // consume -
35852                    let num = self.advance().text;
35853                    expr = Expression::Dot(Box::new(DotAccess {
35854                        this: expr,
35855                        field: Identifier::new(format!("-{}", num)),
35856                    }));
35857                } else {
35858                    return Err(self.parse_error("Expected field name after dot"));
35859                }
35860            } else if self.match_token(TokenType::Collate) {
35861                // Parse COLLATE 'collation_name' or COLLATE "collation_name" or COLLATE collation_name
35862                let (collation, quoted, double_quoted) = if self.check(TokenType::String) {
35863                    // Single-quoted string: COLLATE 'de_DE'
35864                    (self.advance().text, true, false)
35865                } else if self.check(TokenType::QuotedIdentifier) {
35866                    // Double-quoted identifier: COLLATE "de_DE"
35867                    (self.advance().text, false, true)
35868                } else {
35869                    // Unquoted identifier: COLLATE de_DE
35870                    (self.expect_identifier_or_keyword()?, false, false)
35871                };
35872                expr = Expression::Collation(Box::new(CollationExpr {
35873                    this: expr,
35874                    collation,
35875                    quoted,
35876                    double_quoted,
35877                }));
35878            } else if self.check(TokenType::DColon)
35879                || self.check(TokenType::DColonDollar)
35880                || self.check(TokenType::DColonPercent)
35881                || self.check(TokenType::DColonQMark)
35882            {
35883                // For SingleStore, :: variants are JSON path extraction
35884                // For other dialects, :: is cast syntax (PostgreSQL-style)
35885                if matches!(
35886                    self.config.dialect,
35887                    Some(crate::dialects::DialectType::SingleStore)
35888                ) {
35889                    // SingleStore JSON path extraction: expr::key, expr::$key, expr::%key, expr::?key
35890                    if self.match_token(TokenType::DColon) {
35891                        // ::key -> JSON_EXTRACT_JSON(expr, 'key')
35892                        let path_key =
35893                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35894                                self.advance().text
35895                            } else if self.check(TokenType::Number) {
35896                                self.advance().text
35897                            } else if self.check(TokenType::QuotedIdentifier) {
35898                                self.advance().text
35899                            } else {
35900                                return Err(self.parse_error(
35901                                    "Expected identifier or number after :: in JSON path",
35902                                ));
35903                            };
35904                        expr = Expression::Function(Box::new(Function::new(
35905                            "JSON_EXTRACT_JSON".to_string(),
35906                            vec![expr, Expression::string(&path_key)],
35907                        )));
35908                    } else if self.match_token(TokenType::DColonDollar) {
35909                        // ::$key -> JSON_EXTRACT_STRING(expr, 'key')
35910                        let path_key =
35911                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35912                                self.advance().text
35913                            } else if self.check(TokenType::Number) {
35914                                self.advance().text
35915                            } else {
35916                                return Err(self.parse_error(
35917                                    "Expected identifier or number after ::$ in JSON path",
35918                                ));
35919                            };
35920                        expr = Expression::Function(Box::new(Function::new(
35921                            "JSON_EXTRACT_STRING".to_string(),
35922                            vec![expr, Expression::string(&path_key)],
35923                        )));
35924                    } else if self.match_token(TokenType::DColonPercent) {
35925                        // ::%key -> JSON_EXTRACT_DOUBLE(expr, 'key')
35926                        let path_key =
35927                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35928                                self.advance().text
35929                            } else if self.check(TokenType::Number) {
35930                                self.advance().text
35931                            } else {
35932                                return Err(self.parse_error(
35933                                    "Expected identifier or number after ::% in JSON path",
35934                                ));
35935                            };
35936                        expr = Expression::Function(Box::new(Function::new(
35937                            "JSON_EXTRACT_DOUBLE".to_string(),
35938                            vec![expr, Expression::string(&path_key)],
35939                        )));
35940                    } else if self.match_token(TokenType::DColonQMark) {
35941                        // ::?key -> SingleStoreJsonPathQMark function (for JSON_MATCH_ANY patterns)
35942                        let path_key =
35943                            if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
35944                                self.advance().text
35945                            } else if self.check(TokenType::Number) {
35946                                self.advance().text
35947                            } else {
35948                                return Err(self.parse_error(
35949                                    "Expected identifier or number after ::? in JSON path",
35950                                ));
35951                            };
35952                        // Use a special function name that SingleStore generator will recognize
35953                        expr = Expression::Function(Box::new(Function::new(
35954                            "__SS_JSON_PATH_QMARK__".to_string(),
35955                            vec![expr, Expression::string(&path_key)],
35956                        )));
35957                    }
35958                } else {
35959                    // PostgreSQL :: cast operator: expr::type
35960                    self.skip(); // consume DColon
35961                                 // Use parse_data_type_for_cast to avoid consuming subscripts as array dimensions
35962                    let data_type = self.parse_data_type_for_cast()?;
35963                    expr = Expression::Cast(Box::new(Cast {
35964                        this: expr,
35965                        to: data_type,
35966                        trailing_comments: Vec::new(),
35967                        double_colon_syntax: true,
35968                        format: None,
35969                        default: None,
35970                        inferred_type: None,
35971                    }));
35972                }
35973            } else if self.match_token(TokenType::ColonGt) {
35974                // SingleStore :> cast operator: expr :> type
35975                let data_type = self.parse_data_type_for_cast()?;
35976                expr = Expression::Cast(Box::new(Cast {
35977                    this: expr,
35978                    to: data_type,
35979                    trailing_comments: Vec::new(),
35980                    double_colon_syntax: false, // Use :> syntax in generator
35981                    format: None,
35982                    default: None,
35983                    inferred_type: None,
35984                }));
35985            } else if self.match_token(TokenType::NColonGt) {
35986                // SingleStore !:> try cast operator: expr !:> type
35987                let data_type = self.parse_data_type_for_cast()?;
35988                expr = Expression::TryCast(Box::new(Cast {
35989                    this: expr,
35990                    to: data_type,
35991                    trailing_comments: Vec::new(),
35992                    double_colon_syntax: false,
35993                    format: None,
35994                    default: None,
35995                    inferred_type: None,
35996                }));
35997            } else if self.match_token(TokenType::QDColon) {
35998                // Databricks ?:: try cast operator: expr?::type
35999                let data_type = self.parse_data_type_for_cast()?;
36000                expr = Expression::TryCast(Box::new(Cast {
36001                    this: expr,
36002                    to: data_type,
36003                    trailing_comments: Vec::new(),
36004                    double_colon_syntax: true, // Uses :: style syntax
36005                    format: None,
36006                    default: None,
36007                    inferred_type: None,
36008                }));
36009            } else if self.check(TokenType::Arrow)
36010                && !matches!(
36011                    self.config.dialect,
36012                    Some(crate::dialects::DialectType::ClickHouse)
36013                )
36014            {
36015                self.skip(); // consume ->
36016                             // JSON extract 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::JsonExtract(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::DArrow) {
36031                // JSON extract text operator: expr ->> path (PostgreSQL, MySQL, DuckDB)
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::JsonExtractScalar(Box::new(JsonExtractFunc {
36035                    this: expr,
36036                    path,
36037                    returning: None,
36038                    arrow_syntax: true,
36039                    hash_arrow_syntax: false,
36040                    wrapper_option: None,
36041                    quotes_option: None,
36042                    on_scalar_string: false,
36043                    on_error: None,
36044                }));
36045            } else if self.match_token(TokenType::HashArrow) {
36046                // JSONB path extract: expr #> path (PostgreSQL)
36047                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
36048                let path = self.parse_json_path_operand()?;
36049                expr = Expression::JsonExtractPath(Box::new(JsonPathFunc {
36050                    this: expr,
36051                    paths: vec![path],
36052                }));
36053            } else if self.match_token(TokenType::DHashArrow) {
36054                // JSONB path extract text: expr #>> path (PostgreSQL)
36055                // For now, use JsonExtractScalar since the result is text
36056                // Use parse_json_path_operand to get only the immediate operand for proper left-to-right associativity
36057                let path = self.parse_json_path_operand()?;
36058                expr = Expression::JsonExtractScalar(Box::new(JsonExtractFunc {
36059                    this: expr,
36060                    path,
36061                    returning: None,
36062                    arrow_syntax: false,     // This is #>> not ->>
36063                    hash_arrow_syntax: true, // Mark as #>> operator
36064                    wrapper_option: None,
36065                    quotes_option: None,
36066                    on_scalar_string: false,
36067                    on_error: None,
36068                }));
36069            } else if self.check_join_marker() {
36070                // Oracle/Redshift-style outer join marker: column (+)
36071                // Only applies to Column expressions
36072                if let Expression::Column(col) = &mut expr {
36073                    self.skip(); // consume (
36074                    self.skip(); // consume +
36075                    self.skip(); // consume )
36076                    col.join_mark = true;
36077                    // Don't continue - join marker is terminal (no more postfix ops after it)
36078                    break;
36079                }
36080                // If not a Column, just break - the marker is invalid in this context
36081                else {
36082                    break;
36083                }
36084            } else {
36085                break;
36086            }
36087        }
36088        Ok(expr)
36089    }
36090
36091    /// Check if the next tokens are the Oracle-style join marker (+)
36092    fn check_join_marker(&self) -> bool {
36093        self.check(TokenType::LParen)
36094            && self
36095                .peek_nth(1)
36096                .map_or(false, |t| t.token_type == TokenType::Plus)
36097            && self
36098                .peek_nth(2)
36099                .map_or(false, |t| t.token_type == TokenType::RParen)
36100    }
36101
36102    /// Parse OVER clause
36103    fn parse_over_clause(&mut self) -> Result<Over> {
36104        // Handle OVER window_name (without parentheses)
36105        if !self.check(TokenType::LParen) {
36106            // OVER window_name - just a named window reference
36107            let window_name = self.expect_identifier_or_keyword()?;
36108            return Ok(Over {
36109                window_name: Some(Identifier::new(window_name)),
36110                partition_by: Vec::new(),
36111                order_by: Vec::new(),
36112                frame: None,
36113                alias: None,
36114            });
36115        }
36116
36117        self.expect(TokenType::LParen)?;
36118
36119        // Check for named window reference at start of OVER clause
36120        // e.g., OVER (w ORDER BY y) - w is a window name that can be extended
36121        let window_name = if (self.check(TokenType::Identifier)
36122            || self.check(TokenType::Var)
36123            || self.check_keyword())
36124            && !self.check(TokenType::Partition)
36125            && !self.check(TokenType::Order)
36126            && !self.check(TokenType::Rows)
36127            && !self.check(TokenType::Range)
36128            && !self.check(TokenType::Groups)
36129            && !self.check(TokenType::Distribute)
36130            && !self.check(TokenType::Sort)
36131        {
36132            // Look ahead to see if next token indicates this is a window name
36133            let pos = self.current;
36134            let name = self.advance().text;
36135            // If next token is a keyword that can follow a window name, this is a named reference
36136            if self.check(TokenType::Order)
36137                || self.check(TokenType::Partition)
36138                || self.check(TokenType::Rows)
36139                || self.check(TokenType::Range)
36140                || self.check(TokenType::Groups)
36141                || self.check(TokenType::RParen)
36142                || self.check(TokenType::Distribute)
36143                || self.check(TokenType::Sort)
36144            {
36145                Some(Identifier::new(name))
36146            } else {
36147                // Not a named window, restore position
36148                self.current = pos;
36149                None
36150            }
36151        } else {
36152            None
36153        };
36154
36155        // Parse PARTITION BY or DISTRIBUTE BY (Hive uses DISTRIBUTE BY in window specs)
36156        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
36157            self.parse_expression_list()?
36158        } else if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
36159            // Hive: DISTRIBUTE BY is equivalent to PARTITION BY in window specs
36160            self.parse_expression_list()?
36161        } else {
36162            Vec::new()
36163        };
36164
36165        // Parse ORDER BY or SORT BY (Hive uses SORT BY in window specs)
36166        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By])
36167            || self.match_keywords(&[TokenType::Sort, TokenType::By])
36168        {
36169            let mut exprs = Vec::new();
36170            loop {
36171                let expr = self.parse_expression()?;
36172                let (desc, explicit_asc) = if self.match_token(TokenType::Desc) {
36173                    (true, false)
36174                } else if self.match_token(TokenType::Asc) {
36175                    (false, true)
36176                } else {
36177                    (false, false)
36178                };
36179                // ClickHouse/SQL: COLLATE 'collation' in window ORDER BY
36180                if self.match_token(TokenType::Collate) {
36181                    // Consume collation name (string or identifier)
36182                    if self.check(TokenType::String) {
36183                        self.skip();
36184                    } else if self.check(TokenType::QuotedIdentifier) {
36185                        self.skip();
36186                    } else {
36187                        let _ = self.expect_identifier_or_keyword();
36188                    }
36189                }
36190                let nulls_first = if self.match_token(TokenType::Nulls) {
36191                    if self.match_token(TokenType::First) {
36192                        Some(true)
36193                    } else if self.match_token(TokenType::Last) {
36194                        Some(false)
36195                    } else {
36196                        return Err(self.parse_error("Expected FIRST or LAST after NULLS"));
36197                    }
36198                } else {
36199                    None
36200                };
36201                // ClickHouse: WITH FILL in window ORDER BY
36202                let with_fill = if matches!(
36203                    self.config.dialect,
36204                    Some(crate::dialects::DialectType::ClickHouse)
36205                ) && self.check(TokenType::With)
36206                    && self.current + 1 < self.tokens.len()
36207                    && self.tokens[self.current + 1]
36208                        .text
36209                        .eq_ignore_ascii_case("FILL")
36210                {
36211                    self.skip(); // consume WITH
36212                    self.skip(); // consume FILL
36213                    let from_ = if self.match_token(TokenType::From) {
36214                        Some(Box::new(self.parse_or()?))
36215                    } else {
36216                        None
36217                    };
36218                    let to = if self.match_text_seq(&["TO"]) {
36219                        Some(Box::new(self.parse_or()?))
36220                    } else {
36221                        None
36222                    };
36223                    let step = if self.match_text_seq(&["STEP"]) {
36224                        Some(Box::new(self.parse_or()?))
36225                    } else {
36226                        None
36227                    };
36228                    let staleness = if self.match_text_seq(&["STALENESS"]) {
36229                        Some(Box::new(self.parse_or()?))
36230                    } else {
36231                        None
36232                    };
36233                    let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
36234                        if self.match_token(TokenType::LParen) {
36235                            let items = self.parse_expression_list()?;
36236                            self.expect(TokenType::RParen)?;
36237                            if items.len() == 1 {
36238                                Some(Box::new(items.into_iter().next().unwrap()))
36239                            } else {
36240                                Some(Box::new(Expression::Tuple(Box::new(
36241                                    crate::expressions::Tuple { expressions: items },
36242                                ))))
36243                            }
36244                        } else {
36245                            None
36246                        }
36247                    } else {
36248                        None
36249                    };
36250                    Some(Box::new(WithFill {
36251                        from_,
36252                        to,
36253                        step,
36254                        staleness,
36255                        interpolate,
36256                    }))
36257                } else {
36258                    None
36259                };
36260                exprs.push(Ordered {
36261                    this: expr,
36262                    desc,
36263                    nulls_first,
36264                    explicit_asc,
36265                    with_fill,
36266                });
36267                if !self.match_token(TokenType::Comma) {
36268                    break;
36269                }
36270            }
36271            exprs
36272        } else {
36273            Vec::new()
36274        };
36275
36276        // Parse window frame
36277        let frame = self.parse_window_frame()?;
36278
36279        self.expect(TokenType::RParen)?;
36280
36281        Ok(Over {
36282            window_name,
36283            partition_by,
36284            order_by,
36285            frame,
36286            alias: None,
36287        })
36288    }
36289
36290    /// Parse window frame specification (ROWS/RANGE/GROUPS BETWEEN ...)
36291    fn parse_window_frame(&mut self) -> Result<Option<WindowFrame>> {
36292        let (kind, kind_text) = if self.match_token(TokenType::Rows) {
36293            (
36294                WindowFrameKind::Rows,
36295                self.tokens[self.current - 1].text.clone(),
36296            )
36297        } else if self.match_token(TokenType::Range) {
36298            (
36299                WindowFrameKind::Range,
36300                self.tokens[self.current - 1].text.clone(),
36301            )
36302        } else if self.match_token(TokenType::Groups) {
36303            (
36304                WindowFrameKind::Groups,
36305                self.tokens[self.current - 1].text.clone(),
36306            )
36307        } else {
36308            return Ok(None);
36309        };
36310
36311        // Parse BETWEEN or single bound
36312        let (start, start_side_text, end, end_side_text) = if self.match_token(TokenType::Between) {
36313            let (start, st) = self.parse_window_frame_bound()?;
36314            self.expect(TokenType::And)?;
36315            let (end, et) = self.parse_window_frame_bound()?;
36316            (start, st, Some(end), et)
36317        } else {
36318            let (start, st) = self.parse_window_frame_bound()?;
36319            (start, st, None, None)
36320        };
36321
36322        // Parse optional EXCLUDE clause
36323        let exclude = if self.match_token(TokenType::Exclude) {
36324            if self.match_token(TokenType::Current) {
36325                self.expect(TokenType::Row)?;
36326                Some(WindowFrameExclude::CurrentRow)
36327            } else if self.match_token(TokenType::Group) {
36328                Some(WindowFrameExclude::Group)
36329            } else if self.match_token(TokenType::Ties) {
36330                Some(WindowFrameExclude::Ties)
36331            } else if self.match_token(TokenType::No) {
36332                self.expect(TokenType::Others)?;
36333                Some(WindowFrameExclude::NoOthers)
36334            } else {
36335                return Err(self
36336                    .parse_error("Expected CURRENT ROW, GROUP, TIES, or NO OTHERS after EXCLUDE"));
36337            }
36338        } else {
36339            None
36340        };
36341
36342        Ok(Some(WindowFrame {
36343            kind,
36344            start,
36345            end,
36346            exclude,
36347            kind_text: Some(kind_text),
36348            start_side_text,
36349            end_side_text,
36350        }))
36351    }
36352
36353    /// Parse a window frame bound, returning the bound and the original text of the side keyword
36354    fn parse_window_frame_bound(&mut self) -> Result<(WindowFrameBound, Option<String>)> {
36355        if self.match_token(TokenType::Current) {
36356            self.expect(TokenType::Row)?;
36357            Ok((WindowFrameBound::CurrentRow, None))
36358        } else if self.match_token(TokenType::Unbounded) {
36359            if self.match_token(TokenType::Preceding) {
36360                let text = self.tokens[self.current - 1].text.clone();
36361                Ok((WindowFrameBound::UnboundedPreceding, Some(text)))
36362            } else if self.match_token(TokenType::Following) {
36363                let text = self.tokens[self.current - 1].text.clone();
36364                Ok((WindowFrameBound::UnboundedFollowing, Some(text)))
36365            } else {
36366                Err(self.parse_error("Expected PRECEDING or FOLLOWING after UNBOUNDED"))
36367            }
36368        } else if self.match_token(TokenType::Preceding) {
36369            let text = self.tokens[self.current - 1].text.clone();
36370            // PRECEDING [value] (inverted syntax for some dialects)
36371            // If no value follows (e.g., just "PRECEDING" or "PRECEDING)"), use BarePreceding
36372            if self.check(TokenType::RParen) || self.check(TokenType::Comma) {
36373                Ok((WindowFrameBound::BarePreceding, Some(text)))
36374            } else {
36375                let expr = self.parse_primary()?;
36376                Ok((WindowFrameBound::Preceding(Box::new(expr)), Some(text)))
36377            }
36378        } else if self.match_token(TokenType::Following) {
36379            let text = self.tokens[self.current - 1].text.clone();
36380            // FOLLOWING [value] (inverted syntax for some dialects)
36381            // If no value follows (e.g., just "FOLLOWING" or "FOLLOWING)"), use BareFollowing
36382            if self.check(TokenType::RParen) || self.check(TokenType::Comma) {
36383                Ok((WindowFrameBound::BareFollowing, Some(text)))
36384            } else {
36385                let expr = self.parse_primary()?;
36386                Ok((WindowFrameBound::Following(Box::new(expr)), Some(text)))
36387            }
36388        } else {
36389            // <expr> PRECEDING | FOLLOWING (standard syntax)
36390            // Use parse_addition to handle expressions like 1 + 1 PRECEDING
36391            let expr = self.parse_addition()?;
36392            if self.match_token(TokenType::Preceding) {
36393                let text = self.tokens[self.current - 1].text.clone();
36394                Ok((WindowFrameBound::Preceding(Box::new(expr)), Some(text)))
36395            } else if self.match_token(TokenType::Following) {
36396                let text = self.tokens[self.current - 1].text.clone();
36397                Ok((WindowFrameBound::Following(Box::new(expr)), Some(text)))
36398            } else {
36399                // Bare numeric bounds without PRECEDING/FOLLOWING
36400                // (e.g., RANGE BETWEEN 1 AND 3)
36401                Ok((WindowFrameBound::Value(Box::new(expr)), None))
36402            }
36403        }
36404    }
36405
36406    /// Try to parse INTERVAL expression. Returns None if INTERVAL should be treated as identifier.
36407    fn try_parse_interval(&mut self) -> Result<Option<Expression>> {
36408        self.try_parse_interval_internal(true)
36409    }
36410
36411    /// Internal interval parsing that optionally matches the INTERVAL keyword.
36412    /// When match_interval is false, it parses a chained interval value-unit pair
36413    /// without requiring the INTERVAL keyword.
36414    fn try_parse_interval_internal(&mut self, match_interval: bool) -> Result<Option<Expression>> {
36415        let start_pos = self.current;
36416
36417        // Consume the INTERVAL keyword if required
36418        if match_interval {
36419            if !self.check(TokenType::Interval) {
36420                return Ok(None);
36421            }
36422            self.expect(TokenType::Interval)?;
36423
36424            // Check if next token is an operator - if so, INTERVAL is used as identifier
36425            if self.check(TokenType::Eq)
36426                || self.check(TokenType::Neq)
36427                || self.check(TokenType::Lt)
36428                || self.check(TokenType::Gt)
36429                || self.check(TokenType::Lte)
36430                || self.check(TokenType::Gte)
36431                || self.check(TokenType::And)
36432                || self.check(TokenType::Or)
36433                || self.check(TokenType::Is)
36434                || self.check(TokenType::In)
36435                || self.check(TokenType::Like)
36436                || self.check(TokenType::ILike)
36437                || self.check(TokenType::Between)
36438                || self.check(TokenType::Then)
36439                || self.check(TokenType::Else)
36440                || self.check(TokenType::When)
36441                || self.check(TokenType::End)
36442                || self.check(TokenType::Comma)
36443                || self.check(TokenType::RParen)
36444                || self.check(TokenType::DColon)
36445            {
36446                // INTERVAL is used as identifier
36447                self.current = start_pos;
36448                return Ok(None);
36449            }
36450        }
36451
36452        // Parse the value after INTERVAL
36453        // IMPORTANT: For string literals, don't use parse_primary() because it calls
36454        // maybe_parse_subscript() which would consume postfix operators like ::TYPE.
36455        // Those should be applied to the full INTERVAL expression, not just the value inside.
36456        // e.g., INTERVAL '1 hour'::VARCHAR should be CAST(INTERVAL '1 hour' AS VARCHAR)
36457        //       not INTERVAL CAST('1 hour' AS VARCHAR)
36458        // For non-string values, use parse_addition() to handle expressions like
36459        // INTERVAL 2 * 2 MONTH or INTERVAL DAYOFMONTH(dt) - 1 DAY (MySQL syntax)
36460        // This matches Python sqlglot's _parse_term() behavior which handles +, -, *, /, %
36461        let value = if self.check(TokenType::String) {
36462            let token = self.advance();
36463            Some(Expression::Literal(Box::new(Literal::String(token.text))))
36464        } else if !self.is_at_end() && !self.is_statement_terminator() {
36465            Some(self.parse_addition()?)
36466        } else {
36467            None
36468        };
36469
36470        // Check if we should treat INTERVAL as an identifier instead
36471        // This happens when:
36472        // - No value was parsed, OR
36473        // - Value is an unqualified, unquoted column reference AND
36474        //   what follows is NOT a valid interval unit
36475        if let Some(ref val) = value {
36476            if let Expression::Column(col) = val {
36477                // Column without table qualifier
36478                if col.table.is_none() {
36479                    // Check if identifier is quoted
36480                    let is_quoted = col.name.quoted;
36481                    if !is_quoted {
36482                        // Check if next token is a valid interval unit
36483                        if !self.is_valid_interval_unit() && !self.check(TokenType::As) {
36484                            // Backtrack - INTERVAL is used as identifier
36485                            self.current = start_pos;
36486                            return Ok(None);
36487                        }
36488                    }
36489                }
36490            } else if let Expression::Identifier(id) = val {
36491                // Bare identifier without table qualifier
36492                let is_quoted = id.quoted;
36493                if !is_quoted {
36494                    // Check if next token is a valid interval unit
36495                    if !self.is_valid_interval_unit() && !self.check(TokenType::As) {
36496                        // Backtrack - INTERVAL is used as identifier
36497                        self.current = start_pos;
36498                        return Ok(None);
36499                    }
36500                }
36501            }
36502        } else if self.is_at_end() || self.is_statement_terminator() {
36503            // No value, and at end/terminator - INTERVAL is an identifier
36504            self.current = start_pos;
36505            return Ok(None);
36506        }
36507
36508        // Now parse the optional unit
36509        let mut unit = self.try_parse_interval_unit()?;
36510
36511        // Split compound interval strings like '1 day' into value '1' and unit DAY
36512        // This matches Python sqlglot's INTERVAL_STRING_RE behavior
36513        // Only apply in generic mode -- dialects like PostgreSQL preserve compound strings
36514        let is_generic = self.config.dialect.is_none()
36515            || matches!(
36516                self.config.dialect,
36517                Some(crate::dialects::DialectType::Generic)
36518            );
36519        let value = if unit.is_none() && is_generic {
36520            if let Some(Expression::Literal(ref lit)) = value {
36521                if let Literal::String(ref s) = lit.as_ref() {
36522                    let trimmed = s.trim();
36523                    // Match pattern: optional negative sign, digits (optional decimal), space(s), alpha unit
36524                    let mut split_pos = None;
36525                    let mut found_space = false;
36526                    let bytes = trimmed.as_bytes();
36527                    let mut i = 0;
36528                    // Skip optional negative sign
36529                    if i < bytes.len() && bytes[i] == b'-' {
36530                        i += 1;
36531                    }
36532                    // Expect digits
36533                    let digit_start = i;
36534                    while i < bytes.len() && bytes[i].is_ascii_digit() {
36535                        i += 1;
36536                    }
36537                    if i > digit_start {
36538                        // Optional decimal part
36539                        if i < bytes.len() && bytes[i] == b'.' {
36540                            i += 1;
36541                            while i < bytes.len() && bytes[i].is_ascii_digit() {
36542                                i += 1;
36543                            }
36544                        }
36545                        // Expect whitespace
36546                        let space_start = i;
36547                        while i < bytes.len() && bytes[i].is_ascii_whitespace() {
36548                            i += 1;
36549                        }
36550                        if i > space_start {
36551                            found_space = true;
36552                            split_pos = Some(i);
36553                        }
36554                    }
36555                    if found_space {
36556                        if let Some(pos) = split_pos {
36557                            let unit_text = &trimmed[pos..];
36558                            // Verify it's all alpha
36559                            if !unit_text.is_empty()
36560                                && unit_text.chars().all(|c| c.is_ascii_alphabetic())
36561                            {
36562                                let num_part = trimmed[..pos].trim_end().to_string();
36563                                let unit_upper = unit_text.to_ascii_uppercase();
36564                                // Try to parse as interval unit
36565                                if let Some(parsed_unit) =
36566                                    Self::parse_interval_unit_from_string(&unit_upper)
36567                                {
36568                                    // Check if the original text had an 'S' suffix (plural)
36569                                    let is_plural = unit_upper.ends_with('S');
36570                                    unit = Some(IntervalUnitSpec::Simple {
36571                                        unit: parsed_unit,
36572                                        use_plural: is_plural,
36573                                    });
36574                                    Some(Expression::Literal(Box::new(Literal::String(num_part))))
36575                                } else {
36576                                    value
36577                                }
36578                            } else {
36579                                value
36580                            }
36581                        } else {
36582                            value
36583                        }
36584                    } else {
36585                        value
36586                    }
36587                } else {
36588                    None
36589                }
36590            } else {
36591                value
36592            }
36593        } else {
36594            value
36595        };
36596
36597        // Convert number literals to string literals in intervals (canonical form).
36598        // Most dialects support INTERVAL '5' DAY, so we normalize to this form
36599        // for easier transpilation. This matches Python sqlglot's behavior in
36600        // _parse_interval_span: "if this and this.is_number: this = exp.Literal.string(this.to_py())"
36601        let value = match value {
36602            Some(Expression::Literal(lit))
36603                if unit.is_some() && matches!(lit.as_ref(), Literal::Number(_)) =>
36604            {
36605                let Literal::Number(n) = lit.as_ref() else {
36606                    unreachable!()
36607                };
36608                Some(Expression::Literal(Box::new(Literal::String(n.clone()))))
36609            }
36610            other => other,
36611        };
36612
36613        let interval = Expression::Interval(Box::new(Interval { this: value, unit }));
36614
36615        // Support for chained multi-unit interval syntax (Spark/Hive):
36616        // INTERVAL '5' HOURS '30' MINUTES -> INTERVAL '5' HOURS + INTERVAL '30' MINUTES
36617        // This is done by optionally matching a PLUS sign, and if followed by
36618        // another string or number (without INTERVAL keyword), recursively parsing
36619        // and creating an Add expression.
36620        let before_plus = self.current;
36621        let has_plus = self.match_token(TokenType::Plus);
36622
36623        // Check if followed by a STRING or NUMBER (potential chained interval)
36624        if self.check(TokenType::String) || self.check(TokenType::Number) {
36625            // Recursively parse the chained interval without the INTERVAL keyword
36626            if let Some(next_interval) = self.try_parse_interval_internal(false)? {
36627                return Ok(Some(Expression::Add(Box::new(BinaryOp::new(
36628                    interval,
36629                    next_interval,
36630                )))));
36631            }
36632        }
36633
36634        // If we consumed a PLUS but didn't find a chained interval, backtrack
36635        if has_plus {
36636            self.current = before_plus;
36637        }
36638
36639        Ok(Some(interval))
36640    }
36641
36642    /// Check if current token is a valid interval unit
36643    fn is_valid_interval_unit(&self) -> bool {
36644        if self.is_at_end() {
36645            return false;
36646        }
36647        let text = self.peek().text.to_ascii_uppercase();
36648        matches!(
36649            text.as_str(),
36650            "YEAR"
36651                | "YEARS"
36652                | "MONTH"
36653                | "MONTHS"
36654                | "DAY"
36655                | "DAYS"
36656                | "HOUR"
36657                | "HOURS"
36658                | "MINUTE"
36659                | "MINUTES"
36660                | "SECOND"
36661                | "SECONDS"
36662                | "MILLISECOND"
36663                | "MILLISECONDS"
36664                | "MICROSECOND"
36665                | "MICROSECONDS"
36666                | "NANOSECOND"
36667                | "NANOSECONDS"
36668                | "WEEK"
36669                | "WEEKS"
36670                | "QUARTER"
36671                | "QUARTERS"
36672        )
36673    }
36674
36675    /// Check if current token terminates a statement/expression context
36676    fn is_statement_terminator(&self) -> bool {
36677        if self.is_at_end() {
36678            return true;
36679        }
36680        matches!(
36681            self.peek().token_type,
36682            TokenType::Semicolon
36683                | TokenType::RParen
36684                | TokenType::RBracket
36685                | TokenType::Comma
36686                | TokenType::From
36687                | TokenType::Where
36688                | TokenType::GroupBy
36689                | TokenType::Having
36690                | TokenType::OrderBy
36691                | TokenType::Limit
36692                | TokenType::Union
36693                | TokenType::Intersect
36694                | TokenType::Except
36695                | TokenType::End
36696                | TokenType::Then
36697                | TokenType::Else
36698                | TokenType::When
36699        )
36700    }
36701
36702    /// Try to parse interval unit - returns None if no unit present
36703    fn try_parse_interval_unit(&mut self) -> Result<Option<IntervalUnitSpec>> {
36704        // First, check if there's a function (like CURRENT_DATE, CAST(...))
36705        if self.is_function_start() {
36706            let func = self.parse_primary()?;
36707            return Ok(Some(IntervalUnitSpec::Expr(Box::new(func))));
36708        }
36709
36710        // Try to parse a simple unit or span
36711        if let Some((unit, use_plural)) = self.try_parse_simple_interval_unit()? {
36712            // Check for "TO" to make it a span (e.g., YEAR TO MONTH)
36713            // Use lookahead to avoid consuming TO when it's part of WITH FILL
36714            if self.check_keyword_text("TO") {
36715                let saved = self.current;
36716                self.skip(); // consume TO
36717                if let Some((end_unit, _)) = self.try_parse_simple_interval_unit()? {
36718                    return Ok(Some(IntervalUnitSpec::Span(IntervalSpan {
36719                        this: unit,
36720                        expression: end_unit,
36721                    })));
36722                } else {
36723                    // Not followed by a valid interval unit — backtrack
36724                    self.current = saved;
36725                }
36726            }
36727            return Ok(Some(IntervalUnitSpec::Simple { unit, use_plural }));
36728        }
36729
36730        // No unit found
36731        Ok(None)
36732    }
36733
36734    /// Parse an interval unit from a string (used for splitting compound interval strings)
36735    fn parse_interval_unit_from_string(s: &str) -> Option<IntervalUnit> {
36736        // Strip trailing 'S' for plural forms
36737        let base = if s.ends_with('S') && s.len() > 1 {
36738            &s[..s.len() - 1]
36739        } else {
36740            s
36741        };
36742        match base {
36743            "YEAR" => Some(IntervalUnit::Year),
36744            "MONTH" => Some(IntervalUnit::Month),
36745            "DAY" => Some(IntervalUnit::Day),
36746            "HOUR" => Some(IntervalUnit::Hour),
36747            "MINUTE" => Some(IntervalUnit::Minute),
36748            "SECOND" => Some(IntervalUnit::Second),
36749            "MILLISECOND" => Some(IntervalUnit::Millisecond),
36750            "MICROSECOND" => Some(IntervalUnit::Microsecond),
36751            "QUARTER" => Some(IntervalUnit::Quarter),
36752            "WEEK" => Some(IntervalUnit::Week),
36753            _ => None,
36754        }
36755    }
36756
36757    /// Try to parse a simple interval unit (YEAR, MONTH, etc.) - returns (unit, is_plural)
36758    fn try_parse_simple_interval_unit(&mut self) -> Result<Option<(IntervalUnit, bool)>> {
36759        if self.is_at_end() {
36760            return Ok(None);
36761        }
36762
36763        let text_upper = self.peek().text.to_ascii_uppercase();
36764        let result = match text_upper.as_str() {
36765            "YEAR" => Some((IntervalUnit::Year, false)),
36766            "YEARS" => Some((IntervalUnit::Year, true)),
36767            "MONTH" => Some((IntervalUnit::Month, false)),
36768            "MONTHS" => Some((IntervalUnit::Month, true)),
36769            "DAY" => Some((IntervalUnit::Day, false)),
36770            "DAYS" => Some((IntervalUnit::Day, true)),
36771            "HOUR" => Some((IntervalUnit::Hour, false)),
36772            "HOURS" => Some((IntervalUnit::Hour, true)),
36773            "MINUTE" => Some((IntervalUnit::Minute, false)),
36774            "MINUTES" => Some((IntervalUnit::Minute, true)),
36775            "SECOND" => Some((IntervalUnit::Second, false)),
36776            "SECONDS" => Some((IntervalUnit::Second, true)),
36777            "MILLISECOND" => Some((IntervalUnit::Millisecond, false)),
36778            "MILLISECONDS" => Some((IntervalUnit::Millisecond, true)),
36779            "MICROSECOND" => Some((IntervalUnit::Microsecond, false)),
36780            "MICROSECONDS" => Some((IntervalUnit::Microsecond, true)),
36781            "NANOSECOND" => Some((IntervalUnit::Nanosecond, false)),
36782            "NANOSECONDS" => Some((IntervalUnit::Nanosecond, true)),
36783            "QUARTER" => Some((IntervalUnit::Quarter, false)),
36784            "QUARTERS" => Some((IntervalUnit::Quarter, true)),
36785            "WEEK" => Some((IntervalUnit::Week, false)),
36786            "WEEKS" => Some((IntervalUnit::Week, true)),
36787            _ => None,
36788        };
36789
36790        if result.is_some() {
36791            self.skip(); // consume the unit token
36792        }
36793
36794        Ok(result)
36795    }
36796
36797    /// Check if current position starts a function call or no-paren function
36798    fn is_function_start(&self) -> bool {
36799        if self.is_at_end() {
36800            return false;
36801        }
36802        let token_type = self.peek().token_type;
36803
36804        // Check NO_PAREN_FUNCTIONS configuration map
36805        if NO_PAREN_FUNCTIONS.contains(&token_type) {
36806            if !matches!(
36807                self.config.dialect,
36808                Some(crate::dialects::DialectType::ClickHouse)
36809            ) || token_type != TokenType::CurrentTimestamp
36810            {
36811                return true;
36812            }
36813        }
36814
36815        // Cast functions are always functions
36816        if matches!(
36817            token_type,
36818            TokenType::Cast | TokenType::TryCast | TokenType::SafeCast
36819        ) {
36820            return true;
36821        }
36822
36823        // Check NO_PAREN_FUNCTION_NAMES for string-based lookup
36824        // (handles cases where functions are tokenized as Var/Identifier)
36825        let text_upper = self.peek().text.to_ascii_uppercase();
36826        if crate::function_registry::is_no_paren_function_name_upper(text_upper.as_str()) {
36827            if !matches!(
36828                self.config.dialect,
36829                Some(crate::dialects::DialectType::ClickHouse)
36830            ) || text_upper.as_str() != "CURRENT_TIMESTAMP"
36831            {
36832                return true;
36833            }
36834        }
36835
36836        // Identifier followed by left paren (function call)
36837        if self.is_identifier_token() && self.check_next(TokenType::LParen) {
36838            return true;
36839        }
36840
36841        false
36842    }
36843
36844    /// Try to parse Oracle interval span after an expression.
36845    /// Syntax: (expr) DAY[(precision)] TO SECOND[(fractional_precision)]
36846    /// This is used in Oracle for interval expressions like:
36847    /// (SYSTIMESTAMP - order_date) DAY(9) TO SECOND(3)
36848    fn try_parse_oracle_interval_span(&mut self, expr: Expression) -> Result<Expression> {
36849        let start_pos = self.current;
36850
36851        // Check if current token is an interval unit keyword (DAY, HOUR, MINUTE, SECOND, YEAR, MONTH)
36852        let start_unit_name = if !self.is_at_end() {
36853            let text = self.peek().text.to_ascii_uppercase();
36854            if matches!(
36855                text.as_str(),
36856                "DAY" | "HOUR" | "MINUTE" | "SECOND" | "YEAR" | "MONTH"
36857            ) {
36858                Some(text)
36859            } else {
36860                None
36861            }
36862        } else {
36863            None
36864        };
36865
36866        if start_unit_name.is_none() {
36867            return Ok(expr);
36868        }
36869
36870        let start_unit_name = start_unit_name.unwrap();
36871        self.skip(); // consume the unit keyword
36872
36873        // Parse optional precision: DAY(9) or just DAY
36874        let start_unit = if self.match_token(TokenType::LParen) {
36875            // Parse precision
36876            let precision = self.parse_expression()?;
36877            self.expect(TokenType::RParen)?;
36878            // Create a function-like expression for the unit with precision
36879            Expression::Anonymous(Box::new(Anonymous {
36880                this: Box::new(Expression::Identifier(Identifier {
36881                    name: start_unit_name.clone(),
36882                    quoted: false,
36883                    trailing_comments: Vec::new(),
36884                    span: None,
36885                })),
36886                expressions: vec![precision],
36887            }))
36888        } else {
36889            // Simple unit without precision
36890            Expression::Var(Box::new(Var {
36891                this: start_unit_name,
36892            }))
36893        };
36894
36895        // Check for TO keyword
36896        if !self.match_keyword("TO") {
36897            // Not an interval span, backtrack
36898            self.current = start_pos;
36899            return Ok(expr);
36900        }
36901
36902        // Parse end unit
36903        let end_unit_name = if !self.is_at_end() {
36904            let text = self.peek().text.to_ascii_uppercase();
36905            if matches!(
36906                text.as_str(),
36907                "DAY" | "HOUR" | "MINUTE" | "SECOND" | "YEAR" | "MONTH"
36908            ) {
36909                Some(text)
36910            } else {
36911                None
36912            }
36913        } else {
36914            None
36915        };
36916
36917        let end_unit_name = match end_unit_name {
36918            Some(name) => name,
36919            None => {
36920                // No valid end unit, backtrack
36921                self.current = start_pos;
36922                return Ok(expr);
36923            }
36924        };
36925
36926        self.skip(); // consume the end unit keyword
36927
36928        // Parse optional precision for end unit: SECOND(3) or just SECOND
36929        let end_unit = if self.match_token(TokenType::LParen) {
36930            // Parse fractional precision
36931            let precision = self.parse_expression()?;
36932            self.expect(TokenType::RParen)?;
36933            // Create a function-like expression for the unit with precision
36934            Expression::Anonymous(Box::new(Anonymous {
36935                this: Box::new(Expression::Identifier(Identifier {
36936                    name: end_unit_name.clone(),
36937                    quoted: false,
36938                    trailing_comments: Vec::new(),
36939                    span: None,
36940                })),
36941                expressions: vec![precision],
36942            }))
36943        } else {
36944            // Simple unit without precision
36945            Expression::Var(Box::new(Var {
36946                this: end_unit_name,
36947            }))
36948        };
36949
36950        // Create an Interval expression with ExprSpan unit
36951        Ok(Expression::Interval(Box::new(Interval {
36952            this: Some(expr),
36953            unit: Some(IntervalUnitSpec::ExprSpan(IntervalSpanExpr {
36954                this: Box::new(start_unit),
36955                expression: Box::new(end_unit),
36956            })),
36957        })))
36958    }
36959
36960    /// Check if the current position starts a typed column list (for table function aliases)
36961    /// like: (col1 type1, col2 type2)
36962    /// This peeks ahead to see if the first column name is followed by a type token,
36963    /// rather than a comma or closing paren (which would indicate simple column aliases).
36964    /// Used for PostgreSQL functions like JSON_TO_RECORDSET that have typed column definitions.
36965    fn check_typed_column_list(&self) -> bool {
36966        // We're positioned after '(' - check pattern: identifier type
36967        // If we see identifier followed by something that's not ',' or ')', it's typed
36968        if self.is_at_end() {
36969            return false;
36970        }
36971
36972        // Check if current is an identifier (column name)
36973        let has_identifier = self.check(TokenType::Identifier)
36974            || self.check(TokenType::QuotedIdentifier)
36975            || self.check(TokenType::Var);
36976
36977        if !has_identifier {
36978            return false;
36979        }
36980
36981        // Look at next token (after the identifier)
36982        let next_pos = self.current + 1;
36983        if next_pos >= self.tokens.len() {
36984            return false;
36985        }
36986
36987        let next_token = &self.tokens[next_pos];
36988
36989        // If next token is comma or rparen, it's simple column aliases
36990        if next_token.token_type == TokenType::Comma || next_token.token_type == TokenType::RParen {
36991            return false;
36992        }
36993
36994        // If next token could be a type name (identifier, var, or type keyword), it's typed columns
36995        // Check for type tokens or identifiers that could be type names
36996        TYPE_TOKENS.contains(&next_token.token_type)
36997            || next_token.token_type == TokenType::Identifier
36998            || next_token.token_type == TokenType::Var
36999    }
37000
37001    /// Check if current token is a no-paren function
37002    fn is_no_paren_function(&self) -> bool {
37003        if self.is_at_end() {
37004            return false;
37005        }
37006        let token_type = self.peek().token_type;
37007        if NO_PAREN_FUNCTIONS.contains(&token_type) {
37008            if !matches!(
37009                self.config.dialect,
37010                Some(crate::dialects::DialectType::ClickHouse)
37011            ) || token_type != TokenType::CurrentTimestamp
37012            {
37013                return true;
37014            }
37015        }
37016        let text_upper = self.peek().text.to_ascii_uppercase();
37017        if crate::function_registry::is_no_paren_function_name_upper(text_upper.as_str()) {
37018            if !matches!(
37019                self.config.dialect,
37020                Some(crate::dialects::DialectType::ClickHouse)
37021            ) || text_upper.as_str() != "CURRENT_TIMESTAMP"
37022            {
37023                return true;
37024            }
37025        }
37026        false
37027    }
37028
37029    /// Match a keyword by text (case-insensitive)
37030    fn match_keyword(&mut self, keyword: &str) -> bool {
37031        if self.is_at_end() {
37032            return false;
37033        }
37034        if self.peek().text.eq_ignore_ascii_case(keyword) {
37035            self.skip();
37036            true
37037        } else {
37038            false
37039        }
37040    }
37041
37042    /// Match a sequence of keywords by text (case-insensitive)
37043    fn match_text_seq(&mut self, keywords: &[&str]) -> bool {
37044        for (i, &kw) in keywords.iter().enumerate() {
37045            if self.current + i >= self.tokens.len() {
37046                return false;
37047            }
37048            if !self.tokens[self.current + i].text.eq_ignore_ascii_case(kw) {
37049                return false;
37050            }
37051        }
37052        self.current += keywords.len();
37053        true
37054    }
37055
37056    /// Check (without consuming) if the next tokens match a sequence of keywords by text (case-insensitive)
37057    fn check_text_seq(&self, keywords: &[&str]) -> bool {
37058        for (i, &kw) in keywords.iter().enumerate() {
37059            if self.current + i >= self.tokens.len() {
37060                return false;
37061            }
37062            if !self.tokens[self.current + i].text.eq_ignore_ascii_case(kw) {
37063                return false;
37064            }
37065        }
37066        true
37067    }
37068
37069    /// Match any of the given texts (case-insensitive)
37070    fn match_texts(&mut self, texts: &[&str]) -> bool {
37071        if self.is_at_end() {
37072            return false;
37073        }
37074        for text in texts {
37075            if self.peek().text.eq_ignore_ascii_case(text) {
37076                self.skip();
37077                return true;
37078            }
37079        }
37080        false
37081    }
37082
37083    /// Parse CASE expression
37084    fn parse_case(&mut self) -> Result<Expression> {
37085        self.expect(TokenType::Case)?;
37086        // Capture trailing comments from the CASE keyword (e.g., CASE /* test */ WHEN ...)
37087        let case_comments = self.previous_trailing_comments().to_vec();
37088
37089        // Check for simple CASE (CASE expr WHEN ...)
37090        let operand = if !self.check(TokenType::When) {
37091            Some(self.parse_expression()?)
37092        } else {
37093            None
37094        };
37095
37096        let mut whens = Vec::new();
37097        while self.match_token(TokenType::When) {
37098            let condition = self.parse_expression()?;
37099            self.expect(TokenType::Then)?;
37100            let mut result = self.parse_expression()?;
37101            // ClickHouse: CASE WHEN x THEN 1 as alias WHEN y THEN alias / 2 END
37102            // Aliases can appear in CASE THEN expressions
37103            if matches!(
37104                self.config.dialect,
37105                Some(crate::dialects::DialectType::ClickHouse)
37106            ) && self.match_token(TokenType::As)
37107            {
37108                let alias = self.expect_identifier_or_keyword()?;
37109                result = Expression::Alias(Box::new(Alias {
37110                    this: result,
37111                    alias: Identifier::new(alias),
37112                    column_aliases: Vec::new(),
37113                    pre_alias_comments: Vec::new(),
37114                    trailing_comments: Vec::new(),
37115                    inferred_type: None,
37116                }));
37117            }
37118            whens.push((condition, result));
37119        }
37120
37121        let else_ = if self.match_token(TokenType::Else) {
37122            Some(self.parse_expression()?)
37123        } else {
37124            None
37125        };
37126
37127        self.expect(TokenType::End)?;
37128
37129        Ok(Expression::Case(Box::new(Case {
37130            operand,
37131            whens,
37132            else_,
37133            comments: case_comments,
37134            inferred_type: None,
37135        })))
37136    }
37137
37138    /// Parse CAST expression
37139    fn parse_cast(&mut self) -> Result<Expression> {
37140        self.expect(TokenType::Cast)?;
37141        self.expect(TokenType::LParen)?;
37142        // Use parse_or() instead of parse_expression() to avoid consuming AS
37143        // as an alias (e.g. CAST((1, 2) AS Tuple(a Int8, b Int16)))
37144        // Python sqlglot uses _parse_disjunction() here, which is equivalent.
37145        let expr = self.parse_or()?;
37146
37147        // ClickHouse: ternary operator inside CAST: CAST(cond ? true_val : false_val AS Type)
37148        let expr = if matches!(
37149            self.config.dialect,
37150            Some(crate::dialects::DialectType::ClickHouse)
37151        ) && self.match_token(TokenType::Parameter)
37152        {
37153            if self.check(TokenType::Colon) {
37154                return Err(
37155                    self.parse_error("Expected true expression after ? in ClickHouse ternary")
37156                );
37157            }
37158            let true_value = self.parse_or()?;
37159            let false_value = if self.match_token(TokenType::Colon) {
37160                self.parse_or()?
37161            } else {
37162                Expression::Null(Null)
37163            };
37164            Expression::IfFunc(Box::new(IfFunc {
37165                original_name: None,
37166                condition: expr,
37167                true_value,
37168                false_value: Some(false_value),
37169                inferred_type: None,
37170            }))
37171        } else {
37172            expr
37173        };
37174
37175        // ClickHouse: implicit alias in CAST: cast('1234' lhs AS UInt32) or cast('1234' lhs, 'UInt32')
37176        let expr = self.try_clickhouse_implicit_alias(expr);
37177
37178        // ClickHouse: CAST(expr, 'type_string') or CAST(expr, expression) syntax with comma instead of AS
37179        if matches!(
37180            self.config.dialect,
37181            Some(crate::dialects::DialectType::ClickHouse)
37182        ) && self.match_token(TokenType::Comma)
37183        {
37184            // Parse as expression to handle concat and other operations: CAST(x, 'Str' || 'ing')
37185            let type_expr = self.parse_expression()?;
37186            // ClickHouse: alias on type expr: cast('1234' lhs, 'UInt32' rhs) or cast('1234', 'UInt32' AS rhs)
37187            let type_expr = self.try_clickhouse_func_arg_alias(type_expr);
37188            self.expect(TokenType::RParen)?;
37189            let _trailing_comments = self.previous_trailing_comments().to_vec();
37190            return Ok(Expression::CastToStrType(Box::new(CastToStrType {
37191                this: Box::new(expr),
37192                to: Some(Box::new(type_expr)),
37193            })));
37194        }
37195
37196        self.expect(TokenType::As)?;
37197
37198        // ClickHouse: CAST(expr AS alias AS Type) — inner alias before type
37199        // If the next token is an identifier followed by AS, treat it as an alias
37200        let expr = if matches!(
37201            self.config.dialect,
37202            Some(crate::dialects::DialectType::ClickHouse)
37203        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
37204            && self
37205                .peek_nth(1)
37206                .map_or(false, |t| t.token_type == TokenType::As)
37207        {
37208            let alias = self.expect_identifier_or_keyword_with_quoted()?;
37209            self.expect(TokenType::As)?;
37210            Expression::Alias(Box::new(Alias::new(expr, alias)))
37211        } else if matches!(
37212            self.config.dialect,
37213            Some(crate::dialects::DialectType::ClickHouse)
37214        ) && (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
37215            && self
37216                .peek_nth(1)
37217                .map_or(false, |t| t.token_type == TokenType::Comma)
37218        {
37219            // ClickHouse: CAST(expr AS alias, type_string) — alias before comma syntax
37220            let alias = self.expect_identifier_or_keyword_with_quoted()?;
37221            let expr = Expression::Alias(Box::new(Alias::new(expr, alias)));
37222            self.expect(TokenType::Comma)?;
37223            let type_expr = self.parse_expression()?;
37224            let type_expr = self.try_clickhouse_func_arg_alias(type_expr);
37225            self.expect(TokenType::RParen)?;
37226            let _trailing_comments = self.previous_trailing_comments().to_vec();
37227            return Ok(Expression::CastToStrType(Box::new(CastToStrType {
37228                this: Box::new(expr),
37229                to: Some(Box::new(type_expr)),
37230            })));
37231        } else {
37232            expr
37233        };
37234
37235        // Teradata: CAST(x AS FORMAT 'fmt') (no explicit type)
37236        if matches!(
37237            self.config.dialect,
37238            Some(crate::dialects::DialectType::Teradata)
37239        ) && self.match_token(TokenType::Format)
37240        {
37241            let format = Some(Box::new(self.parse_expression()?));
37242            self.expect(TokenType::RParen)?;
37243            let trailing_comments = self.previous_trailing_comments().to_vec();
37244            return Ok(Expression::Cast(Box::new(Cast {
37245                this: expr,
37246                to: DataType::Unknown,
37247                trailing_comments,
37248                double_colon_syntax: false,
37249                format,
37250                default: None,
37251                inferred_type: None,
37252            })));
37253        }
37254
37255        let data_type = self.parse_data_type()?;
37256
37257        // Parse optional DEFAULT ... ON CONVERSION ERROR (Oracle)
37258        // CAST(x AS type DEFAULT val ON CONVERSION ERROR)
37259        let default = if self.match_token(TokenType::Default) {
37260            let default_val = self.parse_primary()?;
37261            // Expect "ON CONVERSION ERROR"
37262            if !self.match_text_seq(&["ON", "CONVERSION", "ERROR"]) {
37263                return Err(self.parse_error("Expected ON CONVERSION ERROR"));
37264            }
37265            Some(Box::new(default_val))
37266        } else {
37267            None
37268        };
37269
37270        // Parse optional FORMAT clause for BigQuery: CAST(x AS STRING FORMAT 'format_string')
37271        // Or for Oracle with comma: CAST(x AS DATE DEFAULT NULL ON CONVERSION ERROR, 'format')
37272        // FORMAT string may be optionally wrapped in parentheses: FORMAT ('YYYY') -> FORMAT 'YYYY'
37273        let format = if self.match_token(TokenType::Format) {
37274            let wrapped = self.match_token(TokenType::LParen);
37275            let fmt_expr = self.parse_primary()?;
37276            if wrapped {
37277                self.expect(TokenType::RParen)?;
37278            }
37279            // Check for AT TIME ZONE after format string
37280            let fmt_with_tz = if self.match_text_seq(&["AT", "TIME", "ZONE"]) {
37281                let zone = self.parse_primary()?;
37282                Expression::AtTimeZone(Box::new(crate::expressions::AtTimeZone {
37283                    this: fmt_expr,
37284                    zone,
37285                }))
37286            } else {
37287                fmt_expr
37288            };
37289            Some(Box::new(fmt_with_tz))
37290        } else if self.match_token(TokenType::Comma) {
37291            // Oracle date format: CAST(x AS DATE, 'format')
37292            Some(Box::new(self.parse_expression()?))
37293        } else {
37294            None
37295        };
37296
37297        self.expect(TokenType::RParen)?;
37298        let trailing_comments = self.previous_trailing_comments().to_vec();
37299
37300        Ok(Expression::Cast(Box::new(Cast {
37301            this: expr,
37302            to: data_type,
37303            trailing_comments,
37304            double_colon_syntax: false,
37305            format,
37306            default,
37307            inferred_type: None,
37308        })))
37309    }
37310
37311    /// Parse TRY_CAST expression
37312    fn parse_try_cast(&mut self) -> Result<Expression> {
37313        self.expect(TokenType::TryCast)?;
37314        self.expect(TokenType::LParen)?;
37315        let expr = self.parse_or()?;
37316        self.expect(TokenType::As)?;
37317        let data_type = self.parse_data_type()?;
37318
37319        // Parse optional FORMAT clause
37320        let format = if self.match_token(TokenType::Format) {
37321            Some(Box::new(self.parse_expression()?))
37322        } else {
37323            None
37324        };
37325
37326        self.expect(TokenType::RParen)?;
37327        let trailing_comments = self.previous_trailing_comments().to_vec();
37328
37329        Ok(Expression::TryCast(Box::new(Cast {
37330            this: expr,
37331            to: data_type,
37332            trailing_comments,
37333            double_colon_syntax: false,
37334            format,
37335            default: None,
37336            inferred_type: None,
37337        })))
37338    }
37339
37340    /// Parse SAFE_CAST expression (BigQuery)
37341    fn parse_safe_cast(&mut self) -> Result<Expression> {
37342        self.expect(TokenType::SafeCast)?;
37343        self.expect(TokenType::LParen)?;
37344        let expr = self.parse_or()?;
37345        self.expect(TokenType::As)?;
37346        let data_type = self.parse_data_type()?;
37347
37348        // Parse optional FORMAT clause
37349        let format = if self.match_token(TokenType::Format) {
37350            Some(Box::new(self.parse_expression()?))
37351        } else {
37352            None
37353        };
37354
37355        self.expect(TokenType::RParen)?;
37356        let trailing_comments = self.previous_trailing_comments().to_vec();
37357
37358        Ok(Expression::SafeCast(Box::new(Cast {
37359            this: expr,
37360            to: data_type,
37361            trailing_comments,
37362            double_colon_syntax: false,
37363            format,
37364            default: None,
37365            inferred_type: None,
37366        })))
37367    }
37368
37369    /// Parse a data type
37370    fn parse_data_type(&mut self) -> Result<DataType> {
37371        // Handle special token types that represent data type keywords
37372        // Teradata tokenizes ST_GEOMETRY as TokenType::Geometry
37373        if self.check(TokenType::Geometry) {
37374            let _token = self.advance();
37375            let (subtype, srid) = self.parse_spatial_type_args()?;
37376            return Ok(DataType::Geometry { subtype, srid });
37377        }
37378        // Data types can be keywords (DATE, TIMESTAMP, etc.) or identifiers
37379        let mut raw_name = self.expect_identifier_or_keyword()?;
37380        // Allow dotted custom types like SYSUDTLIB.INT
37381        while self.match_token(TokenType::Dot) {
37382            let part = self.expect_identifier_or_keyword()?;
37383            raw_name.push('.');
37384            raw_name.push_str(&part);
37385        }
37386        let mut name = raw_name.to_ascii_uppercase();
37387
37388        // SQL standard: NATIONAL CHAR/CHARACTER → NCHAR
37389        if name == "NATIONAL" {
37390            let next_upper = if !self.is_at_end() {
37391                self.peek().text.to_ascii_uppercase()
37392            } else {
37393                String::new()
37394            };
37395            if next_upper == "CHAR" || next_upper == "CHARACTER" {
37396                self.skip(); // consume CHAR/CHARACTER
37397                name = "NCHAR".to_string();
37398                // NATIONAL CHARACTER VARYING → NVARCHAR equivalent
37399                if next_upper == "CHARACTER" && self.check_identifier("VARYING") {
37400                    self.skip(); // consume VARYING
37401                    let length = if self.match_token(TokenType::LParen) {
37402                        if self.check(TokenType::RParen) {
37403                            self.skip();
37404                            None
37405                        } else {
37406                            let n = self.expect_number()? as u32;
37407                            self.expect(TokenType::RParen)?;
37408                            Some(n)
37409                        }
37410                    } else {
37411                        None
37412                    };
37413                    return Ok(DataType::VarChar {
37414                        length,
37415                        parenthesized_length: false,
37416                    });
37417                }
37418            }
37419        }
37420
37421        let base_type = match name.as_str() {
37422            "INT" | "INTEGER" => {
37423                // MySQL allows INT(N) for display width; ClickHouse allows INT()
37424                let length = if self.match_token(TokenType::LParen) {
37425                    if self.check(TokenType::RParen) {
37426                        self.skip();
37427                        None
37428                    } else {
37429                        let n = self.expect_number()? as u32;
37430                        self.expect(TokenType::RParen)?;
37431                        Some(n)
37432                    }
37433                } else {
37434                    None
37435                };
37436                let integer_spelling = name == "INTEGER";
37437                Ok(DataType::Int {
37438                    length,
37439                    integer_spelling,
37440                })
37441            }
37442            "BIGINT" => {
37443                // MySQL allows BIGINT(N) for display width; ClickHouse allows BIGINT()
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::BigInt { length })
37457            }
37458            "SMALLINT" => {
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::SmallInt { length })
37472            }
37473            "TINYINT" => {
37474                let length = if self.match_token(TokenType::LParen) {
37475                    if self.check(TokenType::RParen) {
37476                        self.skip();
37477                        None
37478                    } else {
37479                        let n = self.expect_number()? as u32;
37480                        self.expect(TokenType::RParen)?;
37481                        Some(n)
37482                    }
37483                } else {
37484                    None
37485                };
37486                Ok(DataType::TinyInt { length })
37487            }
37488            "FLOAT" | "REAL" => {
37489                let real_spelling = name == "REAL";
37490                // MySQL allows FLOAT(precision) or FLOAT(precision, scale)
37491                let (precision, scale) = if self.match_token(TokenType::LParen) {
37492                    let p = self.expect_number()? as u32;
37493                    let s = if self.match_token(TokenType::Comma) {
37494                        Some(self.expect_number()? as u32)
37495                    } else {
37496                        None
37497                    };
37498                    self.expect(TokenType::RParen)?;
37499                    (Some(p), s)
37500                } else {
37501                    (None, None)
37502                };
37503                Ok(DataType::Float {
37504                    precision,
37505                    scale,
37506                    real_spelling,
37507                })
37508            }
37509            "BINARY_FLOAT" => {
37510                // Oracle's BINARY_FLOAT -> DataType::Float
37511                Ok(DataType::Float {
37512                    precision: None,
37513                    scale: None,
37514                    real_spelling: false,
37515                })
37516            }
37517            "BINARY_DOUBLE" => {
37518                // Oracle's BINARY_DOUBLE -> DataType::Double
37519                Ok(DataType::Double {
37520                    precision: None,
37521                    scale: None,
37522                })
37523            }
37524            "DOUBLE" => {
37525                // Handle DOUBLE PRECISION (PostgreSQL standard SQL)
37526                let _ = self.match_identifier("PRECISION");
37527                // MySQL allows DOUBLE(precision, scale)
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::Double { precision, scale })
37541            }
37542            "DECIMAL" | "NUMERIC" => {
37543                let (precision, scale) = if self.match_token(TokenType::LParen) {
37544                    let p = self.expect_number()? as u32;
37545                    let s = if self.match_token(TokenType::Comma) {
37546                        Some(self.expect_number()? as u32)
37547                    } else {
37548                        None
37549                    };
37550                    self.expect(TokenType::RParen)?;
37551                    (Some(p), s)
37552                } else {
37553                    (None, None)
37554                };
37555                Ok(DataType::Decimal { precision, scale })
37556            }
37557            "BOOLEAN" | "BOOL" => Ok(DataType::Boolean),
37558            "CHAR" | "CHARACTER" | "NCHAR" => {
37559                let is_nchar = name == "NCHAR";
37560                // SQL standard: CHARACTER LARGE OBJECT → CLOB/TEXT
37561                if self.match_identifier("LARGE") && self.match_identifier("OBJECT") {
37562                    return Ok(DataType::Text);
37563                }
37564                // Check for VARYING to convert to VARCHAR (SQL standard: CHAR VARYING, CHARACTER VARYING)
37565                if self.match_identifier("VARYING") {
37566                    let length = if self.match_token(TokenType::LParen) {
37567                        if self.check(TokenType::RParen) {
37568                            self.skip();
37569                            None
37570                        } else {
37571                            let n = self.expect_number()? as u32;
37572                            self.expect(TokenType::RParen)?;
37573                            Some(n)
37574                        }
37575                    } else {
37576                        None
37577                    };
37578                    Ok(DataType::VarChar {
37579                        length,
37580                        parenthesized_length: false,
37581                    })
37582                } else {
37583                    let length = if self.match_token(TokenType::LParen) {
37584                        // Allow empty parens like NCHAR() - treat as no length specified
37585                        if self.check(TokenType::RParen) {
37586                            self.skip(); // consume RParen
37587                            None
37588                        } else {
37589                            let n = self.expect_number()? as u32;
37590                            self.expect(TokenType::RParen)?;
37591                            Some(n)
37592                        }
37593                    } else {
37594                        None
37595                    };
37596                    // CHAR CHARACTER SET charset (MySQL CAST context, no length)
37597                    // When length is specified (e.g., CHAR(4) CHARACTER SET LATIN),
37598                    // CHARACTER SET is a column attribute handled at the column def level
37599                    if length.is_none()
37600                        && self.match_identifier("CHARACTER")
37601                        && self.match_token(TokenType::Set)
37602                    {
37603                        let charset = self.expect_identifier_or_keyword()?;
37604                        return Ok(DataType::CharacterSet { name: charset });
37605                    }
37606                    // Preserve NCHAR as Custom DataType so target dialects can map it properly
37607                    // (Oracle keeps NCHAR, TSQL keeps NCHAR, others map to CHAR)
37608                    if is_nchar {
37609                        let name = if let Some(len) = length {
37610                            format!("NCHAR({})", len)
37611                        } else {
37612                            "NCHAR".to_string()
37613                        };
37614                        return Ok(DataType::Custom { name });
37615                    }
37616                    Ok(DataType::Char { length })
37617                }
37618            }
37619            "VARCHAR" | "NVARCHAR" => {
37620                let is_nvarchar = name == "NVARCHAR";
37621                if self.match_token(TokenType::LParen) {
37622                    // Allow empty parens like NVARCHAR() - treat as no length specified
37623                    if self.check(TokenType::RParen) {
37624                        self.skip(); // consume RParen
37625                        if is_nvarchar {
37626                            return Ok(DataType::Custom {
37627                                name: "NVARCHAR".to_string(),
37628                            });
37629                        }
37630                        Ok(DataType::VarChar {
37631                            length: None,
37632                            parenthesized_length: false,
37633                        })
37634                    } else if self.check_identifier("MAX") {
37635                        // TSQL: VARCHAR(MAX) / NVARCHAR(MAX)
37636                        self.skip(); // consume MAX
37637                        self.expect(TokenType::RParen)?;
37638                        let type_name = if is_nvarchar {
37639                            "NVARCHAR(MAX)"
37640                        } else {
37641                            "VARCHAR(MAX)"
37642                        };
37643                        Ok(DataType::Custom {
37644                            name: type_name.to_string(),
37645                        })
37646                    } else {
37647                        // Hive allows VARCHAR((50)) - extra parentheses around the length
37648                        let parenthesized_length = self.match_token(TokenType::LParen);
37649                        let n = self.expect_number()? as u32;
37650                        if parenthesized_length {
37651                            self.expect(TokenType::RParen)?;
37652                        }
37653                        self.expect(TokenType::RParen)?;
37654                        // Preserve NVARCHAR as Custom DataType so target dialects can map properly
37655                        if is_nvarchar {
37656                            return Ok(DataType::Custom {
37657                                name: format!("NVARCHAR({})", n),
37658                            });
37659                        }
37660                        Ok(DataType::VarChar {
37661                            length: Some(n),
37662                            parenthesized_length,
37663                        })
37664                    }
37665                } else {
37666                    if is_nvarchar {
37667                        return Ok(DataType::Custom {
37668                            name: "NVARCHAR".to_string(),
37669                        });
37670                    }
37671                    Ok(DataType::VarChar {
37672                        length: None,
37673                        parenthesized_length: false,
37674                    })
37675                }
37676            }
37677            "TEXT" | "NTEXT" => {
37678                // TEXT(n) - optional length parameter
37679                if self.match_token(TokenType::LParen) {
37680                    let n = self.expect_number()? as u32;
37681                    self.expect(TokenType::RParen)?;
37682                    Ok(DataType::TextWithLength { length: n })
37683                } else {
37684                    Ok(DataType::Text)
37685                }
37686            }
37687            "STRING" => {
37688                // BigQuery STRING(n) - parameterized string with max length
37689                let length = if self.match_token(TokenType::LParen) {
37690                    let n = self.expect_number()? as u32;
37691                    self.expect(TokenType::RParen)?;
37692                    Some(n)
37693                } else {
37694                    None
37695                };
37696                Ok(DataType::String { length })
37697            }
37698            "DATE" => Ok(DataType::Date),
37699            "TIME" => {
37700                // ClickHouse: Time('timezone') is a custom type with string arg
37701                if matches!(
37702                    self.config.dialect,
37703                    Some(crate::dialects::DialectType::ClickHouse)
37704                ) && self.check(TokenType::LParen)
37705                    && self.current + 1 < self.tokens.len()
37706                    && self.tokens[self.current + 1].token_type == TokenType::String
37707                {
37708                    self.skip(); // consume LParen
37709                    let args = self.parse_custom_type_args_balanced()?;
37710                    self.expect(TokenType::RParen)?;
37711                    return Ok(DataType::Custom {
37712                        name: format!("Time({})", args),
37713                    });
37714                }
37715                let precision = if self.match_token(TokenType::LParen) {
37716                    if self.check(TokenType::RParen) {
37717                        self.skip();
37718                        None
37719                    } else {
37720                        let p = self.expect_number()? as u32;
37721                        self.expect(TokenType::RParen)?;
37722                        Some(p)
37723                    }
37724                } else {
37725                    None
37726                };
37727                // Handle TIME WITH/WITHOUT TIME ZONE
37728                let timezone = if self.match_token(TokenType::With) {
37729                    self.match_keyword("TIME");
37730                    self.match_keyword("ZONE");
37731                    true
37732                } else if self.match_keyword("WITHOUT") {
37733                    self.match_keyword("TIME");
37734                    self.match_keyword("ZONE");
37735                    false
37736                } else {
37737                    false
37738                };
37739                Ok(DataType::Time {
37740                    precision,
37741                    timezone,
37742                })
37743            }
37744            "TIMETZ" => {
37745                let precision = if self.match_token(TokenType::LParen) {
37746                    let p = self.expect_number()? as u32;
37747                    self.expect(TokenType::RParen)?;
37748                    Some(p)
37749                } else {
37750                    None
37751                };
37752                Ok(DataType::Time {
37753                    precision,
37754                    timezone: true,
37755                })
37756            }
37757            "TIMESTAMP" => {
37758                // Parse optional precision: TIMESTAMP(p)
37759                let precision = if self.match_token(TokenType::LParen) {
37760                    let p = self.expect_number()? as u32;
37761                    self.expect(TokenType::RParen)?;
37762                    Some(p)
37763                } else {
37764                    None
37765                };
37766                // Parse optional WITH/WITHOUT TIME ZONE or WITH LOCAL TIME ZONE
37767                // Note: TIME is a keyword (TokenType::Time) and LOCAL is a keyword (TokenType::Local)
37768                if self.match_token(TokenType::With) {
37769                    // Check for LOCAL TIME ZONE (Exasol) vs TIME ZONE
37770                    // LOCAL is tokenized as TokenType::Local, not as Identifier
37771                    if self.match_token(TokenType::Local) {
37772                        self.match_keyword("TIME");
37773                        self.match_keyword("ZONE");
37774                        // TIMESTAMP WITH LOCAL TIME ZONE - return as custom type for Exasol handling
37775                        Ok(DataType::Custom {
37776                            name: "TIMESTAMPLTZ".to_string(),
37777                        })
37778                    } else {
37779                        self.match_keyword("TIME");
37780                        self.match_keyword("ZONE");
37781                        Ok(DataType::Timestamp {
37782                            precision,
37783                            timezone: true,
37784                        })
37785                    }
37786                } else if self.match_keyword("WITHOUT") {
37787                    self.match_keyword("TIME");
37788                    self.match_keyword("ZONE");
37789                    Ok(DataType::Timestamp {
37790                        precision,
37791                        timezone: false,
37792                    })
37793                } else {
37794                    Ok(DataType::Timestamp {
37795                        precision,
37796                        timezone: false,
37797                    })
37798                }
37799            }
37800            "TIMESTAMPTZ" => {
37801                let precision = if self.match_token(TokenType::LParen) {
37802                    let p = self.expect_number()? as u32;
37803                    self.expect(TokenType::RParen)?;
37804                    Some(p)
37805                } else {
37806                    None
37807                };
37808                Ok(DataType::Timestamp {
37809                    precision,
37810                    timezone: true,
37811                })
37812            }
37813            "TIMESTAMPLTZ" | "TIMESTAMP_LTZ" => {
37814                let precision = if self.match_token(TokenType::LParen) {
37815                    let p = self.expect_number()? as u32;
37816                    self.expect(TokenType::RParen)?;
37817                    Some(p)
37818                } else {
37819                    None
37820                };
37821                let name = if let Some(p) = precision {
37822                    format!("TIMESTAMPLTZ({})", p)
37823                } else {
37824                    "TIMESTAMPLTZ".to_string()
37825                };
37826                Ok(DataType::Custom { name })
37827            }
37828            "INTERVAL" => {
37829                // Parse optional unit (DAYS, DAY, HOUR, etc.)
37830                // Don't consume GENERATED, AS, NOT, NULL, etc. which are column constraints
37831                let unit = if (self.check(TokenType::Identifier)
37832                    || self.check(TokenType::Var)
37833                    || self.check_keyword())
37834                    && !self.check(TokenType::Generated)
37835                    && !self.check(TokenType::As)
37836                    && !self.check(TokenType::Not)
37837                    && !self.check(TokenType::Null)
37838                    && !self.check(TokenType::Default)
37839                    && !self.check(TokenType::PrimaryKey)
37840                    && !self.check(TokenType::Unique)
37841                    && !self.check(TokenType::Check)
37842                    && !self.check(TokenType::Constraint)
37843                    && !self.check(TokenType::References)
37844                    && !self.check(TokenType::Collate)
37845                    && !self.check(TokenType::Comment)
37846                    && !self.check(TokenType::RParen)
37847                    && !self.check(TokenType::Comma)
37848                {
37849                    Some(self.advance().text.to_ascii_uppercase())
37850                } else {
37851                    None
37852                };
37853                // Parse optional TO unit for range intervals like DAY TO HOUR
37854                let to = if self.match_token(TokenType::To) {
37855                    if self.check(TokenType::Identifier)
37856                        || self.check(TokenType::Var)
37857                        || self.check_keyword()
37858                    {
37859                        Some(self.advance().text.to_ascii_uppercase())
37860                    } else {
37861                        None
37862                    }
37863                } else {
37864                    None
37865                };
37866                Ok(DataType::Interval { unit, to })
37867            }
37868            "JSON" => {
37869                if matches!(
37870                    self.config.dialect,
37871                    Some(crate::dialects::DialectType::ClickHouse)
37872                ) && self.match_token(TokenType::LParen)
37873                {
37874                    // ClickHouse: JSON(subcolumn_specs) e.g. JSON(a String, b UInt32) or JSON(max_dynamic_paths=8)
37875                    let args = self.parse_custom_type_args_balanced()?;
37876                    self.expect(TokenType::RParen)?;
37877                    // Uppercase the SKIP keyword in JSON type declarations
37878                    // e.g., "col1 String, skip col2" -> "col1 String, SKIP col2"
37879                    let args = Self::uppercase_json_type_skip_keyword(&args);
37880                    Ok(DataType::Custom {
37881                        name: format!("JSON({})", args),
37882                    })
37883                } else {
37884                    Ok(DataType::Json)
37885                }
37886            }
37887            "JSONB" => Ok(DataType::JsonB),
37888            "UUID" => Ok(DataType::Uuid),
37889            "BLOB" => Ok(DataType::Blob),
37890            "BYTEA" => Ok(DataType::VarBinary { length: None }),
37891            "BIT" => {
37892                let length = if self.match_token(TokenType::LParen) {
37893                    let n = self.expect_number()? as u32;
37894                    self.expect(TokenType::RParen)?;
37895                    Some(n)
37896                } else {
37897                    None
37898                };
37899                Ok(DataType::Bit { length })
37900            }
37901            "VARBIT" | "BIT VARYING" => {
37902                let length = if self.match_token(TokenType::LParen) {
37903                    let n = self.expect_number()? as u32;
37904                    self.expect(TokenType::RParen)?;
37905                    Some(n)
37906                } else {
37907                    None
37908                };
37909                Ok(DataType::VarBit { length })
37910            }
37911            "BINARY" => {
37912                // SQL standard: BINARY LARGE OBJECT → BLOB
37913                if self.match_identifier("LARGE") && self.match_identifier("OBJECT") {
37914                    return Ok(DataType::Blob);
37915                }
37916                // Handle BINARY VARYING (SQL standard for VARBINARY)
37917                if self.match_identifier("VARYING") {
37918                    let length = if self.match_token(TokenType::LParen) {
37919                        let len = self.expect_number()? as u32;
37920                        self.expect(TokenType::RParen)?;
37921                        Some(len)
37922                    } else {
37923                        None
37924                    };
37925                    Ok(DataType::VarBinary { length })
37926                } else {
37927                    let length = if self.match_token(TokenType::LParen) {
37928                        let len = self.expect_number()? as u32;
37929                        self.expect(TokenType::RParen)?;
37930                        Some(len)
37931                    } else {
37932                        None
37933                    };
37934                    Ok(DataType::Binary { length })
37935                }
37936            }
37937            "VARBINARY" => {
37938                let length = if self.match_token(TokenType::LParen) {
37939                    let len = self.expect_number()? as u32;
37940                    self.expect(TokenType::RParen)?;
37941                    Some(len)
37942                } else {
37943                    None
37944                };
37945                Ok(DataType::VarBinary { length })
37946            }
37947            // Generic types with angle bracket or parentheses syntax: ARRAY<T>, ARRAY(T), MAP<K,V>, MAP(K,V)
37948            "ARRAY" => {
37949                if self.match_token(TokenType::Lt) {
37950                    // ARRAY<element_type> - angle bracket style
37951                    let element_type = self.parse_data_type()?;
37952                    self.expect_gt()?;
37953                    Ok(DataType::Array {
37954                        element_type: Box::new(element_type),
37955                        dimension: None,
37956                    })
37957                } else if self.match_token(TokenType::LParen) {
37958                    // ARRAY(element_type) - Snowflake parentheses style
37959                    let element_type = self.parse_data_type()?;
37960                    self.expect(TokenType::RParen)?;
37961                    Ok(DataType::Array {
37962                        element_type: Box::new(element_type),
37963                        dimension: None,
37964                    })
37965                } else {
37966                    // Just ARRAY without type parameter
37967                    Ok(DataType::Custom {
37968                        name: "ARRAY".to_string(),
37969                    })
37970                }
37971            }
37972            "MAP" => {
37973                if self.match_token(TokenType::Lt) {
37974                    // MAP<key_type, value_type> - angle bracket style
37975                    let key_type = self.parse_data_type()?;
37976                    self.expect(TokenType::Comma)?;
37977                    let value_type = self.parse_data_type()?;
37978                    self.expect_gt()?;
37979                    Ok(DataType::Map {
37980                        key_type: Box::new(key_type),
37981                        value_type: Box::new(value_type),
37982                    })
37983                } else if self.match_token(TokenType::LBracket) {
37984                    // Materialize: MAP[TEXT => INT] type syntax
37985                    let key_type = self.parse_data_type()?;
37986                    self.expect(TokenType::FArrow)?;
37987                    let value_type = self.parse_data_type()?;
37988                    self.expect(TokenType::RBracket)?;
37989                    Ok(DataType::Map {
37990                        key_type: Box::new(key_type),
37991                        value_type: Box::new(value_type),
37992                    })
37993                } else if self.match_token(TokenType::LParen) {
37994                    // MAP(key_type, value_type) - Snowflake parentheses style
37995                    let key_type = self.parse_data_type()?;
37996                    self.expect(TokenType::Comma)?;
37997                    let value_type = self.parse_data_type()?;
37998                    self.expect(TokenType::RParen)?;
37999                    Ok(DataType::Map {
38000                        key_type: Box::new(key_type),
38001                        value_type: Box::new(value_type),
38002                    })
38003                } else {
38004                    // Just MAP without type parameters
38005                    Ok(DataType::Custom {
38006                        name: "MAP".to_string(),
38007                    })
38008                }
38009            }
38010            // VECTOR(type, dimension) - Snowflake vector type
38011            // VECTOR(dimension, element_type_alias) or VECTOR(dimension) - SingleStore vector type
38012            "VECTOR" => {
38013                if self.match_token(TokenType::LParen) {
38014                    if self.check(TokenType::Number) {
38015                        // SingleStore format: VECTOR(dimension) or VECTOR(dimension, type_alias)
38016                        let dimension = self.expect_number()? as u32;
38017                        let element_type = if self.match_token(TokenType::Comma) {
38018                            // Parse the type alias (I8, I16, I32, I64, F32, F64)
38019                            let type_alias = self.expect_identifier_or_keyword()?;
38020                            let mapped_type = match type_alias.to_ascii_uppercase().as_str() {
38021                                "I8" => DataType::TinyInt { length: None },
38022                                "I16" => DataType::SmallInt { length: None },
38023                                "I32" => DataType::Int {
38024                                    length: None,
38025                                    integer_spelling: false,
38026                                },
38027                                "I64" => DataType::BigInt { length: None },
38028                                "F32" => DataType::Float {
38029                                    precision: None,
38030                                    scale: None,
38031                                    real_spelling: false,
38032                                },
38033                                "F64" => DataType::Double {
38034                                    precision: None,
38035                                    scale: None,
38036                                },
38037                                _ => DataType::Custom {
38038                                    name: type_alias.to_string(),
38039                                },
38040                            };
38041                            Some(Box::new(mapped_type))
38042                        } else {
38043                            // Just dimension, no type
38044                            None
38045                        };
38046                        self.expect(TokenType::RParen)?;
38047                        Ok(DataType::Vector {
38048                            element_type,
38049                            dimension: Some(dimension),
38050                        })
38051                    } else {
38052                        // Snowflake format: VECTOR(type, dimension)
38053                        let element_type = self.parse_data_type()?;
38054                        self.expect(TokenType::Comma)?;
38055                        let dimension = self.expect_number()? as u32;
38056                        self.expect(TokenType::RParen)?;
38057                        Ok(DataType::Vector {
38058                            element_type: Some(Box::new(element_type)),
38059                            dimension: Some(dimension),
38060                        })
38061                    }
38062                } else {
38063                    Ok(DataType::Custom {
38064                        name: "VECTOR".to_string(),
38065                    })
38066                }
38067            }
38068            // OBJECT(field1 type1, field2 type2, ...) - Snowflake structured object type
38069            "OBJECT" => {
38070                if self.match_token(TokenType::LParen) {
38071                    // ClickHouse: Object('json') — string literal argument
38072                    if matches!(
38073                        self.config.dialect,
38074                        Some(crate::dialects::DialectType::ClickHouse)
38075                    ) && self.check(TokenType::String)
38076                    {
38077                        let arg = self.advance().text;
38078                        self.expect(TokenType::RParen)?;
38079                        return Ok(DataType::Custom {
38080                            name: format!("Object('{}')", arg),
38081                        });
38082                    }
38083                    let mut fields = Vec::new();
38084                    if !self.check(TokenType::RParen) {
38085                        loop {
38086                            let field_name = self.expect_identifier_or_keyword()?;
38087                            let field_type = self.parse_data_type()?;
38088                            // Optional NOT NULL constraint
38089                            let not_null = if self.match_keyword("NOT") {
38090                                // Consume NULL if present
38091                                self.match_keyword("NULL");
38092                                true
38093                            } else {
38094                                false
38095                            };
38096                            fields.push((field_name, field_type, not_null));
38097                            if !self.match_token(TokenType::Comma) {
38098                                break;
38099                            }
38100                        }
38101                    }
38102                    self.expect(TokenType::RParen)?;
38103                    // Check for RENAME FIELDS or ADD FIELDS modifier
38104                    let modifier = if self.match_keyword("RENAME") {
38105                        if self.match_keyword("FIELDS") {
38106                            Some("RENAME FIELDS".to_string())
38107                        } else {
38108                            Some("RENAME".to_string())
38109                        }
38110                    } else if self.match_keyword("ADD") {
38111                        if self.match_keyword("FIELDS") {
38112                            Some("ADD FIELDS".to_string())
38113                        } else {
38114                            Some("ADD".to_string())
38115                        }
38116                    } else {
38117                        None
38118                    };
38119                    Ok(DataType::Object { fields, modifier })
38120                } else {
38121                    Ok(DataType::Custom {
38122                        name: "OBJECT".to_string(),
38123                    })
38124                }
38125            }
38126            "STRUCT" => {
38127                if self.match_token(TokenType::Lt) {
38128                    // STRUCT<field1 type1, field2 type2, ...> - BigQuery angle-bracket syntax
38129                    let fields = self.parse_struct_type_fields(false)?;
38130                    self.expect_gt()?;
38131                    Ok(DataType::Struct {
38132                        fields,
38133                        nested: false,
38134                    })
38135                } else if self.match_token(TokenType::LParen) {
38136                    // STRUCT(field1 type1, field2 type2, ...) - DuckDB parenthesized syntax
38137                    let fields = self.parse_struct_type_fields(true)?;
38138                    self.expect(TokenType::RParen)?;
38139                    Ok(DataType::Struct {
38140                        fields,
38141                        nested: true,
38142                    })
38143                } else {
38144                    // Just STRUCT without type parameters
38145                    Ok(DataType::Custom {
38146                        name: "STRUCT".to_string(),
38147                    })
38148                }
38149            }
38150            "ROW" => {
38151                // ROW(field1 type1, field2 type2, ...) - same as STRUCT with parens
38152                if self.match_token(TokenType::LParen) {
38153                    let fields = self.parse_struct_type_fields(true)?;
38154                    self.expect(TokenType::RParen)?;
38155                    Ok(DataType::Struct {
38156                        fields,
38157                        nested: true,
38158                    })
38159                } else {
38160                    Ok(DataType::Custom {
38161                        name: "ROW".to_string(),
38162                    })
38163                }
38164            }
38165            "RECORD" => {
38166                // RECORD(field1 type1, field2 type2, ...) - SingleStore record type (like ROW/STRUCT)
38167                if self.match_token(TokenType::LParen) {
38168                    let fields = self.parse_struct_type_fields(true)?;
38169                    self.expect(TokenType::RParen)?;
38170                    // Use Struct with nested=true, generator will output RECORD for SingleStore
38171                    Ok(DataType::Struct {
38172                        fields,
38173                        nested: true,
38174                    })
38175                } else {
38176                    Ok(DataType::Custom {
38177                        name: "RECORD".to_string(),
38178                    })
38179                }
38180            }
38181            "ENUM" => {
38182                // ENUM('RED', 'GREEN', 'BLUE') - DuckDB enum type
38183                // ClickHouse: Enum('hello' = 1, 'world' = 2)
38184                // ClickHouse also allows NULL in enum: Enum('a', 'b', NULL)
38185                if self.match_token(TokenType::LParen) {
38186                    let mut values = Vec::new();
38187                    let mut assignments = Vec::new();
38188                    if !self.check(TokenType::RParen) {
38189                        loop {
38190                            let val = if matches!(
38191                                self.config.dialect,
38192                                Some(crate::dialects::DialectType::ClickHouse)
38193                            ) && self.check(TokenType::Null)
38194                            {
38195                                self.skip();
38196                                "NULL".to_string()
38197                            } else {
38198                                self.expect_string()?
38199                            };
38200                            values.push(val);
38201                            // ClickHouse: optional = value assignment (including negative numbers)
38202                            if self.match_token(TokenType::Eq) {
38203                                let negative = self.match_token(TokenType::Dash);
38204                                let num_token = self.advance();
38205                                let val = if negative {
38206                                    format!("-{}", num_token.text)
38207                                } else {
38208                                    num_token.text.clone()
38209                                };
38210                                assignments.push(Some(val));
38211                            } else {
38212                                assignments.push(None);
38213                            }
38214                            if !self.match_token(TokenType::Comma) {
38215                                break;
38216                            }
38217                        }
38218                    }
38219                    self.expect(TokenType::RParen)?;
38220                    Ok(DataType::Enum {
38221                        values,
38222                        assignments,
38223                    })
38224                } else {
38225                    Ok(DataType::Custom {
38226                        name: "ENUM".to_string(),
38227                    })
38228                }
38229            }
38230            "SET" => {
38231                // MySQL SET('a', 'b', 'c') type
38232                if self.match_token(TokenType::LParen) {
38233                    let mut values = Vec::new();
38234                    if !self.check(TokenType::RParen) {
38235                        loop {
38236                            let val = self.expect_string()?;
38237                            values.push(val);
38238                            if !self.match_token(TokenType::Comma) {
38239                                break;
38240                            }
38241                        }
38242                    }
38243                    self.expect(TokenType::RParen)?;
38244                    Ok(DataType::Set { values })
38245                } else {
38246                    Ok(DataType::Custom {
38247                        name: "SET".to_string(),
38248                    })
38249                }
38250            }
38251            "UNION" if self.check(TokenType::LParen) => {
38252                // UNION(num INT, str TEXT) - DuckDB union type (only when followed by paren)
38253                self.skip(); // consume LParen
38254                let struct_fields = self.parse_struct_type_fields(true)?;
38255                self.expect(TokenType::RParen)?;
38256                // Convert StructField to (String, DataType) for Union
38257                let fields: Vec<(String, DataType)> = struct_fields
38258                    .into_iter()
38259                    .map(|f| (f.name, f.data_type))
38260                    .collect();
38261                Ok(DataType::Union { fields })
38262            }
38263            // Spatial types
38264            "GEOMETRY" => {
38265                let (subtype, srid) = self.parse_spatial_type_args()?;
38266                Ok(DataType::Geometry { subtype, srid })
38267            }
38268            "GEOGRAPHY" => {
38269                let (subtype, srid) = self.parse_spatial_type_args()?;
38270                Ok(DataType::Geography { subtype, srid })
38271            }
38272            // MySQL spatial subtypes without wrapper
38273            "POINT" | "LINESTRING" | "POLYGON" | "MULTIPOINT" | "MULTILINESTRING"
38274            | "MULTIPOLYGON" | "GEOMETRYCOLLECTION" => {
38275                // Check for optional SRID clause (MySQL syntax)
38276                let srid = if self.match_identifier("SRID") {
38277                    Some(self.expect_number()? as u32)
38278                } else {
38279                    None
38280                };
38281                Ok(DataType::Geometry {
38282                    subtype: Some(name),
38283                    srid,
38284                })
38285            }
38286            // BigQuery ANY TYPE - templated parameter type for UDFs
38287            "ANY" => {
38288                if self.match_token(TokenType::Type) {
38289                    Ok(DataType::Custom {
38290                        name: "ANY TYPE".to_string(),
38291                    })
38292                } else {
38293                    Ok(DataType::Custom {
38294                        name: "ANY".to_string(),
38295                    })
38296                }
38297            }
38298            // LONG VARCHAR (Exasol) - same as TEXT
38299            "LONG" => {
38300                if self.match_identifier("VARCHAR") {
38301                    Ok(DataType::Text)
38302                } else {
38303                    Ok(DataType::Custom {
38304                        name: "LONG".to_string(),
38305                    })
38306                }
38307            }
38308            // MySQL SIGNED [INTEGER] / UNSIGNED [INTEGER] in CAST context
38309            // CAST(x AS SIGNED INTEGER) -> CAST(x AS SIGNED)
38310            "SIGNED" | "UNSIGNED" => {
38311                // Consume optional INTEGER keyword after SIGNED/UNSIGNED
38312                if self.check_identifier("INTEGER")
38313                    || self.check_keyword_text("INTEGER")
38314                    || self.check_keyword_text("INT")
38315                {
38316                    self.skip();
38317                }
38318                Ok(DataType::Custom { name })
38319            }
38320            // ClickHouse Nullable(T) wrapper type
38321            "NULLABLE" => {
38322                self.expect(TokenType::LParen)?;
38323                let inner = self.parse_data_type()?;
38324                self.expect(TokenType::RParen)?;
38325                Ok(DataType::Nullable {
38326                    inner: Box::new(inner),
38327                })
38328            }
38329            _ => {
38330                // Handle custom types with optional parenthesized precision/args
38331                // e.g., DATETIME2(2), DATETIMEOFFSET(7), NVARCHAR2(100)
38332                // Use uppercase name for known SQL custom types, but preserve original case
38333                // for user-defined type names (e.g., UserDefinedTableType)
38334                let is_known = convert_name_is_known_custom(&name);
38335                let custom_name = if is_known {
38336                    name.clone()
38337                } else {
38338                    raw_name.clone()
38339                };
38340                if self.match_token(TokenType::LParen) {
38341                    if matches!(
38342                        self.config.dialect,
38343                        Some(crate::dialects::DialectType::ClickHouse)
38344                    ) {
38345                        let args = self.parse_custom_type_args_balanced()?;
38346                        self.expect(TokenType::RParen)?;
38347                        Ok(DataType::Custom {
38348                            name: format!("{}({})", custom_name, args),
38349                        })
38350                    } else {
38351                        let mut args = Vec::new();
38352                        let mut after_comma = true; // treat first token as start of new arg
38353                        loop {
38354                            if self.check(TokenType::RParen) {
38355                                break;
38356                            }
38357                            let token = self.advance();
38358                            // If the previous token was space-separated (not comma-separated),
38359                            // append to the last arg. E.g., VARCHAR2(2328 CHAR) -> "2328 CHAR"
38360                            if !after_comma && !args.is_empty() {
38361                                if let Some(last) = args.last_mut() {
38362                                    *last = format!("{} {}", last, token.text);
38363                                }
38364                            } else {
38365                                args.push(token.text.clone());
38366                            }
38367                            after_comma = self.match_token(TokenType::Comma);
38368                        }
38369                        self.expect(TokenType::RParen)?;
38370                        // Include args in the name: DATETIME2(2), VARCHAR2(2328 CHAR)
38371                        Ok(DataType::Custom {
38372                            name: format!("{}({})", custom_name, args.join(", ")),
38373                        })
38374                    }
38375                } else {
38376                    Ok(DataType::Custom { name: custom_name })
38377                }
38378            }
38379        }?;
38380
38381        // UNSIGNED/SIGNED modifiers for integer types (MySQL) are handled
38382        // by the column definition parser which sets col.unsigned = true.
38383        // Do NOT consume them here; the column parser needs to see them.
38384        let mut result_type = base_type;
38385
38386        // Materialize: handle postfix LIST syntax (INT LIST, INT LIST LIST LIST)
38387        let is_materialize = matches!(
38388            self.config.dialect,
38389            Some(crate::dialects::DialectType::Materialize)
38390        );
38391        if is_materialize {
38392            while self.check_identifier("LIST") || self.check(TokenType::List) {
38393                self.skip(); // consume LIST
38394                result_type = DataType::List {
38395                    element_type: Box::new(result_type),
38396                };
38397            }
38398        }
38399
38400        // PostgreSQL array syntax: TYPE[], TYPE[N], TYPE[N][M], etc.
38401        let result_type = self.maybe_parse_array_dimensions(result_type)?;
38402
38403        // ClickHouse: mark string-like standard types as non-nullable by converting to Custom
38404        // This prevents the generator from wrapping them in Nullable() during identity transforms.
38405        // Types parsed from other dialects remain standard and will get Nullable wrapping when
38406        // transpiling to ClickHouse.
38407        if matches!(
38408            self.config.dialect,
38409            Some(crate::dialects::DialectType::ClickHouse)
38410        ) {
38411            return Ok(Self::clickhouse_mark_non_nullable(result_type));
38412        }
38413
38414        Ok(result_type)
38415    }
38416
38417    /// Convert standard types to Custom equivalents for ClickHouse to prevent Nullable wrapping.
38418    /// This mirrors Python sqlglot's behavior of marking ClickHouse-parsed types as non-nullable.
38419    fn clickhouse_mark_non_nullable(dt: DataType) -> DataType {
38420        match dt {
38421            DataType::Text => DataType::Custom {
38422                name: "String".to_string(),
38423            },
38424            DataType::VarChar { .. } => DataType::Custom {
38425                name: "String".to_string(),
38426            },
38427            DataType::Char { .. } => DataType::Custom {
38428                name: "String".to_string(),
38429            },
38430            DataType::String { .. } => DataType::Custom {
38431                name: "String".to_string(),
38432            },
38433            _ => dt,
38434        }
38435    }
38436
38437    /// Parse a data type for cast syntax (::TYPE)
38438    /// For dialects that support fixed-size arrays (like DuckDB), brackets like [3] are
38439    /// parsed as array dimensions (e.g., x::INT[3] means cast to INT[3] array type).
38440    /// For other dialects (like Snowflake), brackets are subscript operations
38441    /// (e.g., x::VARIANT[0] means cast to VARIANT, then subscript with [0]).
38442    fn parse_data_type_for_cast(&mut self) -> Result<DataType> {
38443        // Check if dialect supports array type suffixes (e.g., INT[], VARCHAR[3])
38444        // PostgreSQL: INT[], TEXT[] (no fixed size)
38445        // DuckDB: INT[3] (fixed size arrays)
38446        let supports_array_type_suffix = matches!(
38447            self.config.dialect,
38448            Some(crate::dialects::DialectType::DuckDB)
38449                | Some(crate::dialects::DialectType::PostgreSQL)
38450                | Some(crate::dialects::DialectType::Redshift)
38451        );
38452
38453        // Check if it's a quoted identifier (e.g., "udt") — preserve case and quoting
38454        let is_quoted = self.check(TokenType::QuotedIdentifier);
38455        let raw_name = self.expect_identifier_or_keyword()?;
38456        if is_quoted {
38457            // Check if the quoted name matches a known type — if so, normalize it
38458            let known_type = self.convert_name_to_type(&raw_name);
38459            if let Ok(ref dt) = known_type {
38460                if !matches!(dt, DataType::Custom { .. }) {
38461                    return known_type;
38462                }
38463            }
38464            // Truly custom type — preserve original case with quotes
38465            return Ok(DataType::Custom {
38466                name: format!("\"{}\"", raw_name),
38467            });
38468        }
38469        let name = raw_name.to_ascii_uppercase();
38470
38471        // Handle parametric types like ARRAY<T>, MAP<K,V>
38472        let base_type = match name.as_str() {
38473            "ARRAY" => {
38474                if self.match_token(TokenType::Lt) {
38475                    let element_type = self.parse_data_type()?;
38476                    self.expect_gt()?;
38477                    DataType::Array {
38478                        element_type: Box::new(element_type),
38479                        dimension: None,
38480                    }
38481                } else if self.match_token(TokenType::LParen) {
38482                    // ClickHouse: Array(Type) syntax with parentheses
38483                    let element_type = self.parse_data_type_for_cast()?;
38484                    self.expect(TokenType::RParen)?;
38485                    DataType::Array {
38486                        element_type: Box::new(element_type),
38487                        dimension: None,
38488                    }
38489                } else {
38490                    DataType::Custom { name }
38491                }
38492            }
38493            "MAP" => {
38494                if self.match_token(TokenType::Lt) {
38495                    let key_type = self.parse_data_type()?;
38496                    self.expect(TokenType::Comma)?;
38497                    let value_type = self.parse_data_type()?;
38498                    self.expect_gt()?;
38499                    DataType::Map {
38500                        key_type: Box::new(key_type),
38501                        value_type: Box::new(value_type),
38502                    }
38503                } else if self.match_token(TokenType::LParen) {
38504                    // Snowflake: MAP(key_type, value_type) syntax
38505                    let key_type = self.parse_data_type_for_cast()?;
38506                    self.expect(TokenType::Comma)?;
38507                    let value_type = self.parse_data_type_for_cast()?;
38508                    self.expect(TokenType::RParen)?;
38509                    DataType::Map {
38510                        key_type: Box::new(key_type),
38511                        value_type: Box::new(value_type),
38512                    }
38513                } else if self.match_token(TokenType::LBracket) {
38514                    // Materialize: MAP[TEXT => INT] type syntax
38515                    let key_type = self.parse_data_type_for_cast()?;
38516                    self.expect(TokenType::FArrow)?;
38517                    let value_type = self.parse_data_type_for_cast()?;
38518                    self.expect(TokenType::RBracket)?;
38519                    DataType::Map {
38520                        key_type: Box::new(key_type),
38521                        value_type: Box::new(value_type),
38522                    }
38523                } else {
38524                    DataType::Custom { name }
38525                }
38526            }
38527            "STRUCT" => {
38528                if self.match_token(TokenType::Lt) {
38529                    let fields = self.parse_struct_type_fields(false)?;
38530                    self.expect_gt()?;
38531                    DataType::Struct {
38532                        fields,
38533                        nested: false,
38534                    }
38535                } else if self.match_token(TokenType::LParen) {
38536                    let fields = self.parse_struct_type_fields(true)?;
38537                    self.expect(TokenType::RParen)?;
38538                    DataType::Struct {
38539                        fields,
38540                        nested: true,
38541                    }
38542                } else {
38543                    DataType::Custom { name }
38544                }
38545            }
38546            "ROW" => {
38547                if self.match_token(TokenType::LParen) {
38548                    let fields = self.parse_struct_type_fields(true)?;
38549                    self.expect(TokenType::RParen)?;
38550                    DataType::Struct {
38551                        fields,
38552                        nested: true,
38553                    }
38554                } else {
38555                    DataType::Custom { name }
38556                }
38557            }
38558            "RECORD" => {
38559                // SingleStore RECORD type (like ROW/STRUCT)
38560                if self.match_token(TokenType::LParen) {
38561                    let fields = self.parse_struct_type_fields(true)?;
38562                    self.expect(TokenType::RParen)?;
38563                    DataType::Struct {
38564                        fields,
38565                        nested: true,
38566                    }
38567                } else {
38568                    DataType::Custom { name }
38569                }
38570            }
38571            // Multi-word types that need special handling in cast context
38572            "DOUBLE" => {
38573                // Handle DOUBLE PRECISION
38574                let _ = self.match_identifier("PRECISION");
38575                // ClickHouse/SQL: DOUBLE(precision) or DOUBLE(precision, scale)
38576                let (precision, scale) = if self.match_token(TokenType::LParen) {
38577                    let p = Some(self.expect_number()? as u32);
38578                    let s = if self.match_token(TokenType::Comma) {
38579                        Some(self.expect_number()? as u32)
38580                    } else {
38581                        None
38582                    };
38583                    self.expect(TokenType::RParen)?;
38584                    (p, s)
38585                } else {
38586                    (None, None)
38587                };
38588                DataType::Double { precision, scale }
38589            }
38590            "CHARACTER" | "CHAR" | "NCHAR" => {
38591                // Handle CHARACTER VARYING / CHAR VARYING
38592                if self.match_identifier("VARYING") {
38593                    let length = if self.match_token(TokenType::LParen) {
38594                        let len = Some(self.expect_number()? as u32);
38595                        self.expect(TokenType::RParen)?;
38596                        len
38597                    } else {
38598                        None
38599                    };
38600                    DataType::VarChar {
38601                        length,
38602                        parenthesized_length: false,
38603                    }
38604                } else {
38605                    let length = if self.match_token(TokenType::LParen) {
38606                        let len = Some(self.expect_number()? as u32);
38607                        self.expect(TokenType::RParen)?;
38608                        len
38609                    } else {
38610                        None
38611                    };
38612                    // CHAR CHARACTER SET charset (MySQL CAST context, no length)
38613                    if length.is_none()
38614                        && self.match_identifier("CHARACTER")
38615                        && self.match_token(TokenType::Set)
38616                    {
38617                        let charset = self.expect_identifier_or_keyword()?;
38618                        return Ok(DataType::CharacterSet { name: charset });
38619                    }
38620                    DataType::Char { length }
38621                }
38622            }
38623            "TIME" => {
38624                // Handle TIME(precision) WITH/WITHOUT TIME ZONE
38625                let precision = if self.match_token(TokenType::LParen) {
38626                    let p = Some(self.expect_number()? as u32);
38627                    self.expect(TokenType::RParen)?;
38628                    p
38629                } else {
38630                    None
38631                };
38632                let timezone = if self.match_token(TokenType::With) {
38633                    self.match_keyword("TIME");
38634                    self.match_keyword("ZONE");
38635                    true
38636                } else if self.match_keyword("WITHOUT") {
38637                    self.match_keyword("TIME");
38638                    self.match_keyword("ZONE");
38639                    false
38640                } else {
38641                    false
38642                };
38643                DataType::Time {
38644                    precision,
38645                    timezone,
38646                }
38647            }
38648            "TIMETZ" => {
38649                let precision = if self.match_token(TokenType::LParen) {
38650                    let p = Some(self.expect_number()? as u32);
38651                    self.expect(TokenType::RParen)?;
38652                    p
38653                } else {
38654                    None
38655                };
38656                DataType::Time {
38657                    precision,
38658                    timezone: true,
38659                }
38660            }
38661            "TIMESTAMP" => {
38662                // Handle TIMESTAMP(precision) WITH/WITHOUT TIME ZONE or WITH LOCAL TIME ZONE
38663                let precision = if self.match_token(TokenType::LParen) {
38664                    let p = Some(self.expect_number()? as u32);
38665                    self.expect(TokenType::RParen)?;
38666                    p
38667                } else {
38668                    None
38669                };
38670                // Note: TIME is a keyword (TokenType::Time), so use match_keyword instead of match_identifier
38671                if self.match_token(TokenType::With) {
38672                    // Check for LOCAL TIME ZONE vs TIME ZONE
38673                    if self.match_token(TokenType::Local) {
38674                        self.match_keyword("TIME");
38675                        self.match_keyword("ZONE");
38676                        // TIMESTAMP WITH LOCAL TIME ZONE -> TIMESTAMPLTZ
38677                        DataType::Custom {
38678                            name: "TIMESTAMPLTZ".to_string(),
38679                        }
38680                    } else {
38681                        self.match_keyword("TIME");
38682                        self.match_keyword("ZONE");
38683                        DataType::Timestamp {
38684                            precision,
38685                            timezone: true,
38686                        }
38687                    }
38688                } else if self.match_keyword("WITHOUT") {
38689                    self.match_keyword("TIME");
38690                    self.match_keyword("ZONE");
38691                    DataType::Timestamp {
38692                        precision,
38693                        timezone: false,
38694                    }
38695                } else {
38696                    DataType::Timestamp {
38697                        precision,
38698                        timezone: false,
38699                    }
38700                }
38701            }
38702            "TIMESTAMPTZ" => {
38703                let precision = if self.match_token(TokenType::LParen) {
38704                    let p = self.expect_number()? as u32;
38705                    self.expect(TokenType::RParen)?;
38706                    Some(p)
38707                } else {
38708                    None
38709                };
38710                DataType::Timestamp {
38711                    precision,
38712                    timezone: true,
38713                }
38714            }
38715            "TIMESTAMPLTZ" | "TIMESTAMP_LTZ" => {
38716                let precision = if self.match_token(TokenType::LParen) {
38717                    let p = self.expect_number()? as u32;
38718                    self.expect(TokenType::RParen)?;
38719                    Some(p)
38720                } else {
38721                    None
38722                };
38723                let dt_name = if let Some(p) = precision {
38724                    format!("TIMESTAMPLTZ({})", p)
38725                } else {
38726                    "TIMESTAMPLTZ".to_string()
38727                };
38728                DataType::Custom { name: dt_name }
38729            }
38730            "INTERVAL" => {
38731                // Parse optional unit (DAY, HOUR, etc.) after INTERVAL in cast context
38732                let unit = if (self.check(TokenType::Identifier)
38733                    || self.check(TokenType::Var)
38734                    || self.check_keyword())
38735                    && !self.check(TokenType::RParen)
38736                    && !self.check(TokenType::Comma)
38737                    && !self.check(TokenType::As)
38738                    && !self.check(TokenType::Not)
38739                    && !self.check(TokenType::Null)
38740                {
38741                    Some(self.advance().text.to_ascii_uppercase())
38742                } else {
38743                    None
38744                };
38745                // Parse optional TO unit for range intervals like DAY TO HOUR
38746                let to = if self.match_token(TokenType::To) {
38747                    if self.check(TokenType::Identifier)
38748                        || self.check(TokenType::Var)
38749                        || self.check_keyword()
38750                    {
38751                        Some(self.advance().text.to_ascii_uppercase())
38752                    } else {
38753                        None
38754                    }
38755                } else {
38756                    None
38757                };
38758                DataType::Interval { unit, to }
38759            }
38760            // VARCHAR/NVARCHAR with optional (N) or (MAX) parameter
38761            "VARCHAR" | "NVARCHAR" => {
38762                let is_nvarchar = name == "NVARCHAR";
38763                if self.match_token(TokenType::LParen) {
38764                    if self.check(TokenType::RParen) {
38765                        self.skip();
38766                        DataType::VarChar {
38767                            length: None,
38768                            parenthesized_length: false,
38769                        }
38770                    } else if self.check_identifier("MAX") {
38771                        self.skip();
38772                        self.expect(TokenType::RParen)?;
38773                        let type_name = if is_nvarchar {
38774                            "NVARCHAR(MAX)"
38775                        } else {
38776                            "VARCHAR(MAX)"
38777                        };
38778                        DataType::Custom {
38779                            name: type_name.to_string(),
38780                        }
38781                    } else {
38782                        let n = self.expect_number()? as u32;
38783                        self.expect(TokenType::RParen)?;
38784                        DataType::VarChar {
38785                            length: Some(n),
38786                            parenthesized_length: false,
38787                        }
38788                    }
38789                } else {
38790                    DataType::VarChar {
38791                        length: None,
38792                        parenthesized_length: false,
38793                    }
38794                }
38795            }
38796            // VARBINARY with optional (N) or (MAX) parameter
38797            "VARBINARY" => {
38798                if self.match_token(TokenType::LParen) {
38799                    if self.check(TokenType::RParen) {
38800                        self.skip();
38801                        DataType::VarBinary { length: None }
38802                    } else if self.check_identifier("MAX") {
38803                        self.skip();
38804                        self.expect(TokenType::RParen)?;
38805                        DataType::Custom {
38806                            name: "VARBINARY(MAX)".to_string(),
38807                        }
38808                    } else {
38809                        let n = self.expect_number()? as u32;
38810                        self.expect(TokenType::RParen)?;
38811                        DataType::VarBinary { length: Some(n) }
38812                    }
38813                } else {
38814                    DataType::VarBinary { length: None }
38815                }
38816            }
38817            // DECIMAL/NUMERIC with optional (precision, scale)
38818            "DECIMAL" | "NUMERIC" | "NUMBER" => {
38819                if self.match_token(TokenType::LParen) {
38820                    let precision = Some(self.expect_number()? as u32);
38821                    let scale = if self.match_token(TokenType::Comma) {
38822                        Some(self.expect_number()? as u32)
38823                    } else {
38824                        None
38825                    };
38826                    self.expect(TokenType::RParen)?;
38827                    DataType::Decimal { precision, scale }
38828                } else {
38829                    DataType::Decimal {
38830                        precision: None,
38831                        scale: None,
38832                    }
38833                }
38834            }
38835            // INT/INTEGER/BIGINT/SMALLINT/TINYINT with optional (N) display width
38836            "INT" | "INTEGER" => {
38837                let length = if self.match_token(TokenType::LParen) {
38838                    let n = Some(self.expect_number()? as u32);
38839                    self.expect(TokenType::RParen)?;
38840                    n
38841                } else {
38842                    None
38843                };
38844                DataType::Int {
38845                    length,
38846                    integer_spelling: name == "INTEGER",
38847                }
38848            }
38849            "BIGINT" => {
38850                let length = if self.match_token(TokenType::LParen) {
38851                    let n = Some(self.expect_number()? as u32);
38852                    self.expect(TokenType::RParen)?;
38853                    n
38854                } else {
38855                    None
38856                };
38857                DataType::BigInt { length }
38858            }
38859            "SMALLINT" => {
38860                let length = if self.match_token(TokenType::LParen) {
38861                    let n = Some(self.expect_number()? as u32);
38862                    self.expect(TokenType::RParen)?;
38863                    n
38864                } else {
38865                    None
38866                };
38867                DataType::SmallInt { length }
38868            }
38869            "TINYINT" => {
38870                let length = if self.match_token(TokenType::LParen) {
38871                    let n = Some(self.expect_number()? as u32);
38872                    self.expect(TokenType::RParen)?;
38873                    n
38874                } else {
38875                    None
38876                };
38877                DataType::TinyInt { length }
38878            }
38879            // FLOAT with optional (precision)
38880            "FLOAT" | "REAL" | "BINARY_FLOAT" => {
38881                let (precision, scale) = if self.match_token(TokenType::LParen) {
38882                    let n = Some(self.expect_number()? as u32);
38883                    let s = if self.match_token(TokenType::Comma) {
38884                        Some(self.expect_number()? as u32)
38885                    } else {
38886                        None
38887                    };
38888                    self.expect(TokenType::RParen)?;
38889                    (n, s)
38890                } else {
38891                    (None, None)
38892                };
38893                DataType::Float {
38894                    precision,
38895                    scale,
38896                    real_spelling: name == "REAL",
38897                }
38898            }
38899            "BINARY_DOUBLE" => DataType::Double {
38900                precision: None,
38901                scale: None,
38902            },
38903            // BINARY with optional (length)
38904            "BINARY" => {
38905                let length = if self.match_token(TokenType::LParen) {
38906                    let n = Some(self.expect_number()? as u32);
38907                    self.expect(TokenType::RParen)?;
38908                    n
38909                } else {
38910                    None
38911                };
38912                DataType::Binary { length }
38913            }
38914            // MySQL SIGNED [INTEGER] / UNSIGNED [INTEGER] in CAST context
38915            // CAST(x AS SIGNED INTEGER) -> CAST(x AS SIGNED)
38916            // CAST(x AS UNSIGNED INTEGER) -> CAST(x AS UNSIGNED)
38917            "SIGNED" | "UNSIGNED" => {
38918                // Consume optional INTEGER keyword after SIGNED/UNSIGNED
38919                if self.check_identifier("INTEGER")
38920                    || self.check_keyword_text("INTEGER")
38921                    || self.check_keyword_text("INT")
38922                {
38923                    self.skip();
38924                }
38925                DataType::Custom { name }
38926            }
38927            // ClickHouse Nullable(T) wrapper type
38928            "NULLABLE" => {
38929                self.expect(TokenType::LParen)?;
38930                let inner = self.parse_data_type_for_cast()?;
38931                self.expect(TokenType::RParen)?;
38932                DataType::Nullable {
38933                    inner: Box::new(inner),
38934                }
38935            }
38936            // For simple types, use convert_name_to_type to get proper DataType variants
38937            // This ensures VARCHAR becomes DataType::VarChar, not DataType::Custom
38938            // For user-defined types in generic mode, preserve original case from raw_name
38939            _ => {
38940                let base = self.convert_name_to_type(&name)?;
38941                // ClickHouse: consume parenthesized args for custom types like DateTime('UTC'),
38942                // LowCardinality(String), Variant(String, UInt64), JSON(max_dynamic_paths=8)
38943                if matches!(
38944                    self.config.dialect,
38945                    Some(crate::dialects::DialectType::ClickHouse)
38946                ) && self.check(TokenType::LParen)
38947                    && (matches!(
38948                        base,
38949                        DataType::Custom { .. } | DataType::Json | DataType::JsonB
38950                    ))
38951                {
38952                    self.skip(); // consume (
38953                    let args = self.parse_custom_type_args_balanced()?;
38954                    self.expect(TokenType::RParen)?;
38955                    let base_name = match &base {
38956                        DataType::Json => "JSON".to_string(),
38957                        DataType::JsonB => "JSONB".to_string(),
38958                        DataType::Custom { name } => name.clone(),
38959                        _ => unreachable!(),
38960                    };
38961                    DataType::Custom {
38962                        name: format!("{}({})", base_name, args),
38963                    }
38964                } else if matches!(base, DataType::Custom { .. }) && self.check(TokenType::Dot) {
38965                    // Handle schema-qualified user-defined types (e.g., app.status_enum)
38966                    // by consuming dot-separated identifiers like Python sqlglot's
38967                    // _parse_user_defined_type()
38968                    // Use raw_name to preserve original case for schema-qualified types
38969                    let mut type_name = raw_name.to_string();
38970                    while self.match_token(TokenType::Dot) {
38971                        let tok = self.advance();
38972                        type_name = format!("{}.{}", type_name, tok.text);
38973                    }
38974                    DataType::Custom { name: type_name }
38975                } else if matches!(base, DataType::Custom { .. }) && self.config.dialect.is_none() {
38976                    // Preserve original case for user-defined types in generic mode
38977                    DataType::Custom {
38978                        name: raw_name.to_string(),
38979                    }
38980                } else {
38981                    base
38982                }
38983            }
38984        };
38985
38986        // Materialize: handle postfix LIST syntax (INT LIST, INT LIST LIST LIST)
38987        let is_materialize = matches!(
38988            self.config.dialect,
38989            Some(crate::dialects::DialectType::Materialize)
38990        );
38991        let mut result_type = base_type;
38992        if is_materialize {
38993            while self.check_identifier("LIST") || self.check(TokenType::List) {
38994                self.skip(); // consume LIST
38995                result_type = DataType::List {
38996                    element_type: Box::new(result_type),
38997                };
38998            }
38999        }
39000
39001        // For dialects that support array type suffixes (DuckDB, PostgreSQL, Redshift),
39002        // parse array dimensions. For other dialects, brackets after a cast are subscript operations.
39003        if supports_array_type_suffix {
39004            self.maybe_parse_array_dimensions(result_type)
39005        } else {
39006            Ok(result_type)
39007        }
39008    }
39009
39010    /// Parse custom type arguments with balanced parentheses, preserving nested types
39011    fn parse_custom_type_args_balanced(&mut self) -> Result<String> {
39012        let mut depth = 0usize;
39013        let mut out = String::new();
39014        let mut prev_wordish = false;
39015
39016        while !self.is_at_end() {
39017            if self.check(TokenType::RParen) && depth == 0 {
39018                break;
39019            }
39020
39021            let token = self.advance();
39022            match token.token_type {
39023                TokenType::LParen => {
39024                    out.push('(');
39025                    depth += 1;
39026                    prev_wordish = false;
39027                }
39028                TokenType::RParen => {
39029                    if depth == 0 {
39030                        break;
39031                    }
39032                    depth -= 1;
39033                    out.push(')');
39034                    prev_wordish = true;
39035                }
39036                TokenType::Comma => {
39037                    out.push_str(", ");
39038                    prev_wordish = false;
39039                }
39040                TokenType::Eq => {
39041                    out.push_str(" = ");
39042                    prev_wordish = false;
39043                }
39044                TokenType::Plus => {
39045                    out.push_str(" + ");
39046                    prev_wordish = false;
39047                }
39048                TokenType::Dash => {
39049                    out.push('-');
39050                    prev_wordish = false;
39051                }
39052                TokenType::Dot => {
39053                    out.push('.');
39054                    prev_wordish = false;
39055                }
39056                TokenType::String | TokenType::DollarString => {
39057                    if prev_wordish {
39058                        out.push(' ');
39059                    }
39060                    let escaped = token.text.replace('\'', "''");
39061                    out.push('\'');
39062                    out.push_str(&escaped);
39063                    out.push('\'');
39064                    prev_wordish = true;
39065                }
39066                TokenType::Number | TokenType::Parameter => {
39067                    if prev_wordish {
39068                        out.push(' ');
39069                    }
39070                    out.push_str(&token.text);
39071                    prev_wordish = true;
39072                }
39073                TokenType::QuotedIdentifier => {
39074                    if prev_wordish {
39075                        out.push(' ');
39076                    }
39077                    out.push('"');
39078                    out.push_str(&token.text);
39079                    out.push('"');
39080                    prev_wordish = true;
39081                }
39082                _ => {
39083                    if prev_wordish {
39084                        out.push(' ');
39085                    }
39086                    out.push_str(&token.text);
39087                    prev_wordish = true;
39088                }
39089            }
39090        }
39091
39092        Ok(out)
39093    }
39094
39095    /// Uppercase the `skip` keyword in ClickHouse JSON type declarations.
39096    /// In ClickHouse, `SKIP col` within JSON(...) type specs must use uppercase SKIP.
39097    fn uppercase_json_type_skip_keyword(args: &str) -> String {
39098        // Replace "skip " at the start of the string or after ", " with "SKIP "
39099        let mut result = String::with_capacity(args.len());
39100        let mut rest = args;
39101        let mut at_start = true;
39102        while !rest.is_empty() {
39103            if at_start
39104                && rest.len() >= 5
39105                && rest[..4].eq_ignore_ascii_case("skip")
39106                && rest.as_bytes()[4] == b' '
39107            {
39108                result.push_str("SKIP");
39109                rest = &rest[4..];
39110                at_start = false;
39111            } else if rest.starts_with(", ") {
39112                result.push_str(", ");
39113                rest = &rest[2..];
39114                at_start = true;
39115            } else {
39116                result.push(rest.as_bytes()[0] as char);
39117                rest = &rest[1..];
39118                at_start = false;
39119            }
39120        }
39121        result
39122    }
39123
39124    /// Parse a data type from a text string by tokenizing and sub-parsing it.
39125    /// Used for ClickHouse JSON path types where a quoted identifier like "Array(JSON)"
39126    /// needs to be parsed as a proper structured DataType.
39127    fn parse_data_type_from_text(&mut self, text: &str) -> Result<DataType> {
39128        use crate::tokens::Tokenizer;
39129        let tokenizer = Tokenizer::default();
39130        let tokens = tokenizer.tokenize(text)?;
39131        if tokens.is_empty() {
39132            return Ok(DataType::Custom {
39133                name: text.to_string(),
39134            });
39135        }
39136        // Save parser state and temporarily swap in the sub-tokens
39137        let saved_tokens = std::mem::replace(&mut self.tokens, tokens);
39138        let saved_current = std::mem::replace(&mut self.current, 0);
39139        let result = self.parse_data_type();
39140        // Restore original parser state
39141        self.tokens = saved_tokens;
39142        self.current = saved_current;
39143        result
39144    }
39145
39146    /// Try to parse a data type optionally - returns None if no valid type found
39147    /// Used for JSON_TABLE column definitions where type may or may not be present
39148    fn parse_data_type_optional(&mut self) -> Result<Option<DataType>> {
39149        // Check if current token looks like a type name
39150        if !self.check(TokenType::Identifier)
39151            && !self.check(TokenType::Var)
39152            && !self.check_keyword()
39153        {
39154            return Ok(None);
39155        }
39156
39157        // Don't try to parse PATH as a type
39158        if self.check_identifier("PATH") {
39159            return Ok(None);
39160        }
39161
39162        // ClickHouse: ALIAS, EPHEMERAL, MATERIALIZED are column modifiers, not types
39163        if matches!(
39164            self.config.dialect,
39165            Some(crate::dialects::DialectType::ClickHouse)
39166        ) && (self.check_identifier("ALIAS")
39167            || self.check_identifier("EPHEMERAL")
39168            || self.check(TokenType::Materialized))
39169        {
39170            return Ok(None);
39171        }
39172
39173        let saved_pos = self.current;
39174        match self.parse_data_type() {
39175            Ok(dt) => Ok(Some(dt)),
39176            Err(_) => {
39177                self.current = saved_pos;
39178                Ok(None)
39179            }
39180        }
39181    }
39182
39183    /// Convert a DataType to a string representation for JSONColumnDef.kind
39184    fn data_type_to_string(&self, dt: &DataType) -> String {
39185        match dt {
39186            DataType::Int {
39187                length: Some(n),
39188                integer_spelling: true,
39189            } => format!("INTEGER({})", n),
39190            DataType::Int {
39191                length: Some(n), ..
39192            } => format!("INT({})", n),
39193            DataType::Int {
39194                length: None,
39195                integer_spelling: true,
39196            } => "INTEGER".to_string(),
39197            DataType::Int { length: None, .. } => "INT".to_string(),
39198            DataType::BigInt { length: Some(n) } => format!("BIGINT({})", n),
39199            DataType::BigInt { length: None } => "BIGINT".to_string(),
39200            DataType::SmallInt { length: Some(n) } => format!("SMALLINT({})", n),
39201            DataType::SmallInt { length: None } => "SMALLINT".to_string(),
39202            DataType::TinyInt { length: Some(n) } => format!("TINYINT({})", n),
39203            DataType::TinyInt { length: None } => "TINYINT".to_string(),
39204            DataType::Float {
39205                precision: Some(p),
39206                scale: Some(s),
39207                ..
39208            } => format!("FLOAT({}, {})", p, s),
39209            DataType::Float {
39210                precision: Some(p),
39211                scale: None,
39212                ..
39213            } => format!("FLOAT({})", p),
39214            DataType::Float {
39215                precision: None, ..
39216            } => "FLOAT".to_string(),
39217            DataType::Double {
39218                precision: Some(p),
39219                scale: Some(s),
39220            } => format!("DOUBLE({}, {})", p, s),
39221            DataType::Double {
39222                precision: Some(p),
39223                scale: None,
39224            } => format!("DOUBLE({})", p),
39225            DataType::Double {
39226                precision: None, ..
39227            } => "DOUBLE".to_string(),
39228            DataType::Decimal {
39229                precision: Some(p),
39230                scale: Some(s),
39231            } => format!("DECIMAL({}, {})", p, s),
39232            DataType::Decimal {
39233                precision: Some(p),
39234                scale: None,
39235            } => format!("DECIMAL({})", p),
39236            DataType::Decimal {
39237                precision: None, ..
39238            } => "DECIMAL".to_string(),
39239            DataType::VarChar {
39240                length: Some(n), ..
39241            } => format!("VARCHAR({})", n),
39242            DataType::VarChar { length: None, .. } => "VARCHAR".to_string(),
39243            DataType::Char { length: Some(n) } => format!("CHAR({})", n),
39244            DataType::Char { length: None } => "CHAR".to_string(),
39245            DataType::Text => "TEXT".to_string(),
39246            DataType::Boolean => "BOOLEAN".to_string(),
39247            DataType::Date => "DATE".to_string(),
39248            DataType::Time {
39249                precision: Some(p), ..
39250            } => format!("TIME({})", p),
39251            DataType::Time {
39252                precision: None, ..
39253            } => "TIME".to_string(),
39254            DataType::Timestamp {
39255                precision: Some(p),
39256                timezone: true,
39257            } => format!("TIMESTAMPTZ({})", p),
39258            DataType::Timestamp {
39259                precision: Some(p),
39260                timezone: false,
39261            } => format!("TIMESTAMP({})", p),
39262            DataType::Timestamp {
39263                precision: None,
39264                timezone: true,
39265            } => "TIMESTAMPTZ".to_string(),
39266            DataType::Timestamp {
39267                precision: None,
39268                timezone: false,
39269            } => "TIMESTAMP".to_string(),
39270            DataType::Json => "JSON".to_string(),
39271            DataType::JsonB => "JSONB".to_string(),
39272            DataType::Binary { length: Some(n) } => format!("BINARY({})", n),
39273            DataType::Binary { length: None } => "BINARY".to_string(),
39274            DataType::VarBinary { length: Some(n) } => format!("VARBINARY({})", n),
39275            DataType::VarBinary { length: None } => "VARBINARY".to_string(),
39276            DataType::String { length: Some(n) } => format!("STRING({})", n),
39277            DataType::String { length: None } => "STRING".to_string(),
39278            DataType::Array { element_type, .. } => {
39279                format!("ARRAY({})", self.data_type_to_string(element_type))
39280            }
39281            DataType::Nullable { inner } => {
39282                format!("Nullable({})", self.data_type_to_string(inner))
39283            }
39284            DataType::Custom { name } => name.clone(),
39285            _ => format!("{:?}", dt),
39286        }
39287    }
39288
39289    /// Parse optional array dimensions after a type: [], [N], [N][M], ARRAY, ARRAY[N], etc.
39290    fn maybe_parse_array_dimensions(&mut self, base_type: DataType) -> Result<DataType> {
39291        let mut current_type = base_type;
39292
39293        // Handle PostgreSQL ARRAY keyword suffix: type ARRAY or type ARRAY[3]
39294        if self.check_identifier("ARRAY") {
39295            self.skip(); // consume ARRAY
39296                         // Check for optional dimension: ARRAY[N]
39297            let dimension = if self.match_token(TokenType::LBracket) {
39298                let dim = if self.check(TokenType::Number) {
39299                    let n = self.expect_number()? as u32;
39300                    Some(n)
39301                } else {
39302                    None
39303                };
39304                self.expect(TokenType::RBracket)?;
39305                dim
39306            } else {
39307                None
39308            };
39309            current_type = DataType::Array {
39310                element_type: Box::new(current_type),
39311                dimension,
39312            };
39313        }
39314
39315        // Handle bracket-based array dimensions: TYPE[], TYPE[N], TYPE[][N], etc.
39316        while self.match_token(TokenType::LBracket) {
39317            // Check for optional dimension: [N] or just []
39318            let dimension = if self.check(TokenType::Number) {
39319                let n = self.expect_number()? as u32;
39320                Some(n)
39321            } else {
39322                None
39323            };
39324            self.expect(TokenType::RBracket)?;
39325
39326            current_type = DataType::Array {
39327                element_type: Box::new(current_type),
39328                dimension,
39329            };
39330        }
39331
39332        Ok(current_type)
39333    }
39334
39335    /// Parse spatial type arguments like GEOMETRY(Point, 4326) or GEOGRAPHY
39336    fn parse_spatial_type_args(&mut self) -> Result<(Option<String>, Option<u32>)> {
39337        if self.match_token(TokenType::LParen) {
39338            // First arg can be a subtype name (POINT, LINESTRING, etc.) or a numeric dimension
39339            if self.check(TokenType::Number) {
39340                // Numeric argument (e.g., ST_GEOMETRY(1) in Teradata)
39341                let n = self.expect_number()? as u32;
39342                self.expect(TokenType::RParen)?;
39343                return Ok((None, Some(n)));
39344            }
39345            // Parse subtype
39346            let subtype = Some(self.expect_identifier()?.to_ascii_uppercase());
39347
39348            // Parse optional SRID
39349            let srid = if self.match_token(TokenType::Comma) {
39350                Some(self.expect_number()? as u32)
39351            } else {
39352                None
39353            };
39354
39355            self.expect(TokenType::RParen)?;
39356            Ok((subtype, srid))
39357        } else {
39358            Ok((None, None))
39359        }
39360    }
39361
39362    /// Parse struct/row/union type fields: name TYPE, name TYPE, ...
39363    /// `paren_style` indicates whether we're parsing parenthesized syntax (terminates at RParen)
39364    /// or angle-bracket syntax (terminates at Gt/GtGt).
39365    fn parse_struct_type_fields(&mut self, paren_style: bool) -> Result<Vec<StructField>> {
39366        let mut fields = Vec::new();
39367        // Check for empty field list
39368        if (paren_style && self.check(TokenType::RParen))
39369            || (!paren_style && (self.check(TokenType::Gt) || self.check(TokenType::GtGt)))
39370        {
39371            return Ok(fields);
39372        }
39373        loop {
39374            // Parse field name or just type (for anonymous struct fields)
39375            // Track whether it was a quoted identifier to preserve quoting
39376            let is_quoted = self.check(TokenType::QuotedIdentifier);
39377            let first = self.expect_identifier_or_keyword()?;
39378            let first_upper = first.to_ascii_uppercase();
39379
39380            // Check if this is a parametric type (ARRAY<T>, MAP<K,V>, STRUCT<...>, STRUCT(...))
39381            let is_parametric_type = (first_upper == "ARRAY"
39382                || first_upper == "MAP"
39383                || first_upper == "STRUCT"
39384                || first_upper == "ROW")
39385                && (self.check(TokenType::Lt) || self.check(TokenType::LParen));
39386
39387            let (field_name, field_type) = if is_parametric_type {
39388                // This is a parametric type as an anonymous field
39389                let field_type = self.parse_data_type_from_name(&first_upper)?;
39390                (String::new(), field_type)
39391            } else if self.check(TokenType::Comma)
39392                || self.match_identifier("OPTIONS")  // Check for OPTIONS (but don't consume yet)
39393                || (paren_style && self.check(TokenType::RParen))
39394                || (!paren_style && (self.check(TokenType::Gt) || self.check(TokenType::GtGt)))
39395            {
39396                // Check if we just matched OPTIONS - if so, retreat
39397                if self.previous().text.eq_ignore_ascii_case("OPTIONS") {
39398                    self.current -= 1;
39399                }
39400                // Anonymous field: just a type name
39401                let field_type = self.convert_name_to_type(&first)?;
39402                (String::new(), field_type)
39403            } else if self.is_identifier_token()
39404                || self.is_safe_keyword_as_identifier()
39405                || self.check(TokenType::Lt)
39406                || self.check(TokenType::LParen)
39407                || self.check(TokenType::Colon)
39408            {
39409                // Named field: fieldname TYPE (or fieldname: TYPE for Hive)
39410                // Consume optional colon separator (Hive-style: `STRUCT<field_name: TYPE>`)
39411                self.match_token(TokenType::Colon);
39412                let field_type = self.parse_data_type()?;
39413                // Preserve quoting for field names
39414                let field_name = if is_quoted {
39415                    format!("\"{}\"", first)
39416                } else {
39417                    first
39418                };
39419                (field_name, field_type)
39420            } else {
39421                // Just a type name
39422                let field_type = self.convert_name_to_type(&first)?;
39423                (String::new(), field_type)
39424            };
39425
39426            // Spark/Databricks: Check for COMMENT clause on struct field
39427            let comment = if self.match_token(TokenType::Comment) {
39428                Some(self.expect_string()?)
39429            } else {
39430                None
39431            };
39432
39433            // BigQuery: Check for OPTIONS clause on struct field
39434            let options = if self.match_identifier("OPTIONS") {
39435                self.parse_options_list()?
39436            } else {
39437                Vec::new()
39438            };
39439
39440            fields.push(StructField::with_options_and_comment(
39441                field_name, field_type, options, comment,
39442            ));
39443
39444            if !self.match_token(TokenType::Comma) {
39445                break;
39446            }
39447        }
39448        Ok(fields)
39449    }
39450
39451    /// Parse a data type given a name that was already consumed
39452    /// This is used for standalone type expressions like ARRAY<T>
39453    fn parse_data_type_from_name(&mut self, name: &str) -> Result<DataType> {
39454        match name {
39455            "ARRAY" => {
39456                if self.match_token(TokenType::Lt) {
39457                    let element_type = self.parse_data_type()?;
39458                    self.expect_gt()?;
39459                    Ok(DataType::Array {
39460                        element_type: Box::new(element_type),
39461                        dimension: None,
39462                    })
39463                } else {
39464                    Ok(DataType::Custom {
39465                        name: "ARRAY".to_string(),
39466                    })
39467                }
39468            }
39469            "MAP" => {
39470                if self.match_token(TokenType::Lt) {
39471                    let key_type = self.parse_data_type()?;
39472                    self.expect(TokenType::Comma)?;
39473                    let value_type = self.parse_data_type()?;
39474                    self.expect_gt()?;
39475                    Ok(DataType::Map {
39476                        key_type: Box::new(key_type),
39477                        value_type: Box::new(value_type),
39478                    })
39479                } else {
39480                    Ok(DataType::Custom {
39481                        name: "MAP".to_string(),
39482                    })
39483                }
39484            }
39485            "STRUCT" => {
39486                if self.match_token(TokenType::Lt) {
39487                    let fields = self.parse_struct_type_fields(false)?;
39488                    self.expect_gt()?;
39489                    Ok(DataType::Struct {
39490                        fields,
39491                        nested: false,
39492                    })
39493                } else if self.match_token(TokenType::LParen) {
39494                    let fields = self.parse_struct_type_fields(true)?;
39495                    self.expect(TokenType::RParen)?;
39496                    Ok(DataType::Struct {
39497                        fields,
39498                        nested: true,
39499                    })
39500                } else {
39501                    Ok(DataType::Custom {
39502                        name: "STRUCT".to_string(),
39503                    })
39504                }
39505            }
39506            "ROW" => {
39507                if self.match_token(TokenType::LParen) {
39508                    let fields = self.parse_struct_type_fields(true)?;
39509                    self.expect(TokenType::RParen)?;
39510                    Ok(DataType::Struct {
39511                        fields,
39512                        nested: true,
39513                    })
39514                } else {
39515                    Ok(DataType::Custom {
39516                        name: "ROW".to_string(),
39517                    })
39518                }
39519            }
39520            _ => Ok(DataType::Custom {
39521                name: name.to_string(),
39522            }),
39523        }
39524    }
39525
39526    /// Convert a type name string to a DataType
39527    /// Used for anonymous struct fields where we have just a type name
39528    fn convert_name_to_type(&self, name: &str) -> Result<DataType> {
39529        let upper = name.to_ascii_uppercase();
39530        Ok(match upper.as_str() {
39531            "INT" => DataType::Int {
39532                length: None,
39533                integer_spelling: false,
39534            },
39535            "INTEGER" => DataType::Int {
39536                length: None,
39537                integer_spelling: true,
39538            },
39539            "BIGINT" => DataType::BigInt { length: None },
39540            "SMALLINT" => DataType::SmallInt { length: None },
39541            "TINYINT" => DataType::TinyInt { length: None },
39542            "FLOAT" | "BINARY_FLOAT" => DataType::Float {
39543                precision: None,
39544                scale: None,
39545                real_spelling: false,
39546            },
39547            "REAL" => DataType::Float {
39548                precision: None,
39549                scale: None,
39550                real_spelling: true,
39551            },
39552            "DOUBLE" | "BINARY_DOUBLE" => DataType::Double {
39553                precision: None,
39554                scale: None,
39555            },
39556            "DECIMAL" | "NUMERIC" => DataType::Decimal {
39557                precision: None,
39558                scale: None,
39559            },
39560            "BOOLEAN" | "BOOL" => DataType::Boolean,
39561            "CHAR" | "CHARACTER" | "NCHAR" => DataType::Char { length: None },
39562            "VARCHAR" | "NVARCHAR" => DataType::VarChar {
39563                length: None,
39564                parenthesized_length: false,
39565            },
39566            "TEXT" | "STRING" | "NTEXT" => DataType::Text,
39567            "DATE" => DataType::Date,
39568            "TIME" => DataType::Time {
39569                precision: None,
39570                timezone: false,
39571            },
39572            "TIMETZ" => DataType::Time {
39573                precision: None,
39574                timezone: true,
39575            },
39576            "TIMESTAMP" => DataType::Timestamp {
39577                precision: None,
39578                timezone: false,
39579            },
39580            "INTERVAL" => DataType::Interval {
39581                unit: None,
39582                to: None,
39583            },
39584            "JSON" => DataType::Json,
39585            "JSONB" => DataType::JsonB,
39586            "UUID" => DataType::Uuid,
39587            "BLOB" => DataType::Blob,
39588            "BYTEA" => DataType::VarBinary { length: None },
39589            "BINARY" => DataType::Binary { length: None },
39590            "VARBINARY" => DataType::VarBinary { length: None },
39591            "BIT" => DataType::Bit { length: None },
39592            "VARBIT" => DataType::VarBit { length: None },
39593            _ => DataType::Custom {
39594                name: name.to_string(),
39595            },
39596        })
39597    }
39598
39599    /// Parse star modifiers: EXCLUDE/EXCEPT, REPLACE, RENAME
39600    /// Syntax varies by dialect:
39601    /// - DuckDB: * EXCLUDE (col1, col2)
39602    /// - BigQuery: * EXCEPT (col1, col2), * REPLACE (expr AS col)
39603    /// - Snowflake: * EXCLUDE col, * RENAME (old AS new)
39604    fn parse_star_modifiers(&mut self, table: Option<Identifier>) -> Result<Star> {
39605        self.parse_star_modifiers_with_comments(table, Vec::new())
39606    }
39607
39608    /// Parse star modifiers with explicit trailing comments from the star token
39609    fn parse_star_modifiers_with_comments(
39610        &mut self,
39611        table: Option<Identifier>,
39612        star_trailing_comments: Vec<String>,
39613    ) -> Result<Star> {
39614        let mut except = None;
39615        let mut replace = None;
39616        let mut rename = None;
39617
39618        // Parse EXCLUDE / EXCEPT clause
39619        if self.match_token(TokenType::Exclude) || self.match_token(TokenType::Except) {
39620            // ClickHouse: EXCEPT STRICT col1, col2 (STRICT is optional modifier)
39621            let _ = self.match_text_seq(&["STRICT"]);
39622            let mut columns = Vec::new();
39623            if self.match_token(TokenType::LParen) {
39624                // EXCLUDE (col1, col2) or EXCEPT (A.COL_1, B.COL_2)
39625                loop {
39626                    // ClickHouse: allow string literals in EXCEPT ('col_regex')
39627                    // and keywords like 'key', 'index' as column names
39628                    let col = if self.check(TokenType::String) {
39629                        self.advance().text
39630                    } else if self.is_safe_keyword_as_identifier() {
39631                        self.advance().text
39632                    } else {
39633                        self.expect_identifier()?
39634                    };
39635                    // Handle qualified column names like A.COL_1
39636                    if self.match_token(TokenType::Dot) {
39637                        let subcol = if self.is_safe_keyword_as_identifier() {
39638                            self.advance().text
39639                        } else {
39640                            self.expect_identifier()?
39641                        };
39642                        columns.push(Identifier::new(format!("{}.{}", col, subcol)));
39643                    } else {
39644                        columns.push(Identifier::new(col));
39645                    }
39646                    if !self.match_token(TokenType::Comma) {
39647                        break;
39648                    }
39649                }
39650                self.expect(TokenType::RParen)?;
39651            } else {
39652                // EXCLUDE col (single column, Snowflake) or EXCEPT col1, col2 (ClickHouse)
39653                // or EXCEPT 'regex' (ClickHouse)
39654                loop {
39655                    let col = if self.check(TokenType::String) {
39656                        self.advance().text
39657                    } else if self.is_safe_keyword_as_identifier() {
39658                        self.advance().text
39659                    } else {
39660                        self.expect_identifier()?
39661                    };
39662                    columns.push(Identifier::new(col));
39663                    // ClickHouse allows comma-separated columns without parens: EXCEPT col1, col2
39664                    // But only if the next token after comma looks like a column name
39665                    if !matches!(
39666                        self.config.dialect,
39667                        Some(crate::dialects::DialectType::ClickHouse)
39668                    ) || !self.check(TokenType::Comma)
39669                        || !matches!(
39670                            self.peek_nth(1).map(|t| t.token_type),
39671                            Some(TokenType::Identifier)
39672                                | Some(TokenType::QuotedIdentifier)
39673                                | Some(TokenType::Var)
39674                                | Some(TokenType::String)
39675                        )
39676                    {
39677                        break;
39678                    }
39679                    self.skip(); // consume comma
39680                }
39681            }
39682            except = Some(columns);
39683        }
39684
39685        // Parse REPLACE clause
39686        if self.match_token(TokenType::Replace) {
39687            // ClickHouse: REPLACE STRICT is optional modifier
39688            let _ = self.match_text_seq(&["STRICT"]);
39689            let mut replacements = Vec::new();
39690            if self.match_token(TokenType::LParen) {
39691                loop {
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                    if !self.match_token(TokenType::Comma) {
39697                        break;
39698                    }
39699                }
39700                self.expect(TokenType::RParen)?;
39701            } else if matches!(
39702                self.config.dialect,
39703                Some(crate::dialects::DialectType::ClickHouse)
39704            ) {
39705                // ClickHouse: REPLACE [STRICT] expr AS name (single entry without parens)
39706                // Multiple entries require parens: REPLACE(expr1 AS name1, expr2 AS name2)
39707                let expr = self.parse_expression()?;
39708                self.expect(TokenType::As)?;
39709                let alias = self.expect_identifier_or_keyword()?;
39710                replacements.push(Alias::new(expr, Identifier::new(alias)));
39711            } else {
39712                return Err(self.parse_error("Expected LParen after REPLACE"));
39713            }
39714            replace = Some(replacements);
39715        }
39716
39717        // Parse RENAME clause (Snowflake)
39718        if self.match_token(TokenType::Rename) {
39719            let mut renames = Vec::new();
39720            if self.match_token(TokenType::LParen) {
39721                loop {
39722                    let old_name = self.expect_identifier()?;
39723                    self.expect(TokenType::As)?;
39724                    let new_name = self.expect_identifier()?;
39725                    renames.push((Identifier::new(old_name), Identifier::new(new_name)));
39726                    if !self.match_token(TokenType::Comma) {
39727                        break;
39728                    }
39729                }
39730                self.expect(TokenType::RParen)?;
39731            } else {
39732                // Single rename without parens
39733                let old_name = self.expect_identifier()?;
39734                self.expect(TokenType::As)?;
39735                let new_name = self.expect_identifier()?;
39736                renames.push((Identifier::new(old_name), Identifier::new(new_name)));
39737            }
39738            rename = Some(renames);
39739        }
39740
39741        Ok(Star {
39742            table,
39743            except,
39744            replace,
39745            rename,
39746            trailing_comments: star_trailing_comments,
39747            span: None,
39748        })
39749    }
39750
39751    // === Helper methods ===
39752
39753    /// Check if at end of tokens
39754    #[inline]
39755    fn is_at_end(&self) -> bool {
39756        self.current >= self.tokens.len()
39757    }
39758
39759    /// Check if current token is a query modifier keyword or end of input.
39760    /// Used after GROUP BY ALL/DISTINCT to decide whether to parse expression lists.
39761    fn is_at_query_modifier_or_end(&self) -> bool {
39762        if self.is_at_end() {
39763            return true;
39764        }
39765        matches!(
39766            self.peek().token_type,
39767            TokenType::Having
39768                | TokenType::Qualify
39769                | TokenType::Window
39770                | TokenType::Order
39771                | TokenType::Limit
39772                | TokenType::Fetch
39773                | TokenType::Offset
39774                | TokenType::For
39775                | TokenType::Lock
39776                | TokenType::Union
39777                | TokenType::Except
39778                | TokenType::Intersect
39779                | TokenType::RParen
39780                | TokenType::Semicolon
39781                | TokenType::Where
39782        )
39783    }
39784
39785    /// Create a parse error with position from the current token
39786    fn parse_error(&self, message: impl Into<String>) -> Error {
39787        let span = self.peek().span;
39788        Error::parse(message, span.line, span.column, span.start, span.end)
39789    }
39790
39791    /// Peek at current token
39792    /// Returns reference to current token, or last token if at end
39793    #[inline]
39794    fn peek(&self) -> &Token {
39795        if self.current >= self.tokens.len() {
39796            // Return last token as fallback when at end
39797            // In practice, callers should check is_at_end() before calling peek()
39798            // but this prevents panic
39799            self.tokens.last().expect("Token list should not be empty")
39800        } else {
39801            &self.tokens[self.current]
39802        }
39803    }
39804
39805    /// Look ahead by n positions (0 = current token)
39806    fn peek_nth(&self, n: usize) -> Option<&Token> {
39807        let idx = self.current + n;
39808        if idx < self.tokens.len() {
39809            Some(&self.tokens[idx])
39810        } else {
39811            None
39812        }
39813    }
39814
39815    /// Advance to next token
39816    #[inline]
39817    fn advance(&mut self) -> Token {
39818        if self.current >= self.tokens.len() {
39819            // Return last token as fallback if we're past the end
39820            // In practice, callers should check is_at_end() before calling advance()
39821            return self
39822                .tokens
39823                .last()
39824                .cloned()
39825                .expect("Token list should not be empty");
39826        }
39827        let token = self.tokens[self.current].clone();
39828        self.current += 1;
39829        token
39830    }
39831
39832    /// Advance to next token without returning it (when result is unused)
39833    #[inline]
39834    fn skip(&mut self) {
39835        if self.current < self.tokens.len() {
39836            self.current += 1;
39837        }
39838    }
39839
39840    /// Get the previous token (last consumed)
39841    fn previous(&self) -> &Token {
39842        &self.tokens[self.current - 1]
39843    }
39844
39845    /// Get trailing comments from the previous token
39846    fn previous_trailing_comments(&self) -> &[String] {
39847        if self.current > 0 {
39848            &self.tokens[self.current - 1].trailing_comments
39849        } else {
39850            &[]
39851        }
39852    }
39853
39854    /// Get the token type of the previous token (the one before current).
39855    fn previous_token_type(&self) -> Option<TokenType> {
39856        if self.current > 0 {
39857            Some(self.tokens[self.current - 1].token_type.clone())
39858        } else {
39859            None
39860        }
39861    }
39862
39863    /// Wrap a query expression in a Subquery node.
39864    /// Only wraps if the expression is a query statement (Select, Union, etc.),
39865    /// not for simple expressions like column references.
39866    fn maybe_wrap_in_subquery(&self, inner: Expression) -> Expression {
39867        if matches!(
39868            &inner,
39869            Expression::Select(_)
39870                | Expression::Union(_)
39871                | Expression::Intersect(_)
39872                | Expression::Except(_)
39873        ) {
39874            Expression::Subquery(Box::new(Subquery {
39875                this: inner,
39876                alias: None,
39877                column_aliases: Vec::new(),
39878                order_by: None,
39879                limit: None,
39880                offset: None,
39881                distribute_by: None,
39882                sort_by: None,
39883                cluster_by: None,
39884                lateral: false,
39885                modifiers_inside: false,
39886                trailing_comments: Vec::new(),
39887                inferred_type: None,
39888            }))
39889        } else {
39890            inner
39891        }
39892    }
39893
39894    /// Clear trailing_comments from the rightmost leaf of an expression tree.
39895    /// Used by parse_and/parse_or to avoid comment duplication: when the same comment
39896    /// is captured both in an expression's trailing_comments (during parse_primary) and
39897    /// in a BinaryOp's operator_comments (during parse_and/parse_or), we clear the
39898    /// expression's copy since the operator_comments position (after AND/OR) is correct.
39899    fn clear_rightmost_trailing_comments(expr: &mut Expression) {
39900        match expr {
39901            Expression::Column(col) => col.trailing_comments.clear(),
39902            Expression::And(op) | Expression::Or(op) => {
39903                Self::clear_rightmost_trailing_comments(&mut op.right);
39904            }
39905            Expression::Not(op) => {
39906                Self::clear_rightmost_trailing_comments(&mut op.this);
39907            }
39908            // For comparison ops, the rightmost is the right operand
39909            Expression::Eq(op)
39910            | Expression::Neq(op)
39911            | Expression::Lt(op)
39912            | Expression::Lte(op)
39913            | Expression::Gt(op)
39914            | Expression::Gte(op)
39915            | Expression::Add(op)
39916            | Expression::Sub(op)
39917            | Expression::Mul(op)
39918            | Expression::Div(op) => {
39919                Self::clear_rightmost_trailing_comments(&mut op.right);
39920            }
39921            // For other expressions, trailing_comments might be stored differently
39922            // We don't need to handle all variants, just the common ones that appear
39923            // as operands in AND/OR expressions
39924            _ => {}
39925        }
39926    }
39927
39928    /// Get leading comments from the current token (comments that appeared before it)
39929    fn current_leading_comments(&self) -> &[String] {
39930        if !self.is_at_end() {
39931            &self.tokens[self.current].comments
39932        } else {
39933            &[]
39934        }
39935    }
39936
39937    /// Convert a slice of tokens to SQL string with proper quoting for strings
39938    fn tokens_to_sql(&self, start: usize, end: usize) -> String {
39939        let mut result = String::new();
39940        let mut prev_line: Option<usize> = None;
39941        let mut prev_end_offset: Option<usize> = None;
39942
39943        for t in &self.tokens[start..end] {
39944            // Check if we moved to a new line (preserve original line structure)
39945            let is_new_line = prev_line.is_some() && t.span.line > prev_line.unwrap();
39946
39947            // Use byte offsets to determine original spacing between tokens.
39948            // This preserves the exact spacing from the source (e.g., TRANSFORM( vs OPTIONS ())
39949            if is_new_line {
39950                result.push('\n');
39951                // Preserve original indentation
39952                // span.column is the column AFTER the last character (1-based),
39953                // so start column = span.column - text.chars().count()
39954                let text_len = t.text.chars().count();
39955                let start_col = t.span.column.saturating_sub(text_len);
39956                // For string tokens, add 2 for the quotes that were stripped
39957                let start_col = if t.token_type == TokenType::String {
39958                    start_col.saturating_sub(2)
39959                } else {
39960                    start_col
39961                };
39962                let indent = if start_col > 1 { start_col - 1 } else { 0 };
39963                for _ in 0..indent {
39964                    result.push(' ');
39965                }
39966            } else if !result.is_empty() {
39967                // Same line: use byte offsets to detect if there was whitespace
39968                let had_space = prev_end_offset.map_or(false, |prev_end| t.span.start > prev_end);
39969                if had_space {
39970                    result.push(' ');
39971                }
39972            }
39973
39974            if t.token_type == TokenType::String {
39975                // Re-add quotes around string literals
39976                result.push('\'');
39977                result.push_str(&t.text.replace('\'', "''"));
39978                result.push('\'');
39979            } else {
39980                result.push_str(&t.text);
39981            }
39982
39983            prev_line = Some(t.span.line);
39984            prev_end_offset = Some(t.span.end);
39985        }
39986        result
39987    }
39988
39989    /// Convert tokens to SQL for CREATE STAGE, normalizing FILE_FORMAT clause
39990    /// Transforms FILE_FORMAT='value' to FILE_FORMAT=(FORMAT_NAME='value')
39991    /// and FILE_FORMAT=schema.format to FILE_FORMAT=(FORMAT_NAME=schema.format)
39992    fn tokens_to_sql_stage_format(&self, start: usize, end: usize) -> String {
39993        let mut result = String::new();
39994        let mut prev_token_type: Option<TokenType> = None;
39995        let mut i = start;
39996
39997        while i < end {
39998            let t = &self.tokens[i];
39999
40000            // Check for FILE_FORMAT= pattern that needs normalization
40001            // FILE_FORMAT must be followed by = and then NOT by (
40002            if (t.token_type == TokenType::Var || t.token_type == TokenType::Identifier)
40003                && t.text.eq_ignore_ascii_case("FILE_FORMAT")
40004                && i + 1 < end
40005                && self.tokens[i + 1].token_type == TokenType::Eq
40006                && (i + 2 >= end || self.tokens[i + 2].token_type != TokenType::LParen)
40007            {
40008                // Need to normalize: FILE_FORMAT=value -> FILE_FORMAT=(FORMAT_NAME=value)
40009                if !result.is_empty() && prev_token_type != Some(TokenType::LParen) {
40010                    result.push(' ');
40011                }
40012                result.push_str("FILE_FORMAT=(FORMAT_NAME=");
40013
40014                // Skip FILE_FORMAT and =
40015                i += 2;
40016
40017                // Collect the value (string literal or qualified identifier like schema.format)
40018                while i < end {
40019                    let val = &self.tokens[i];
40020                    if val.token_type == TokenType::String {
40021                        // String literal: 'format1'
40022                        result.push('\'');
40023                        result.push_str(&val.text.replace('\'', "''"));
40024                        result.push('\'');
40025                        i += 1;
40026                        break;
40027                    } else if val.token_type == TokenType::Var
40028                        || val.token_type == TokenType::Identifier
40029                    {
40030                        // Identifier: schema1 or format1
40031                        result.push_str(&val.text);
40032                        i += 1;
40033                        // Check for dot (qualified name)
40034                        if i < end && self.tokens[i].token_type == TokenType::Dot {
40035                            result.push('.');
40036                            i += 1;
40037                            // Expect identifier after dot
40038                            if i < end {
40039                                result.push_str(&self.tokens[i].text);
40040                                i += 1;
40041                            }
40042                        }
40043                        break;
40044                    } else {
40045                        break;
40046                    }
40047                }
40048                result.push(')');
40049                prev_token_type = Some(TokenType::RParen);
40050                continue;
40051            }
40052
40053            // Normal token handling (same as tokens_to_sql)
40054            let needs_space = !result.is_empty()
40055                && prev_token_type != Some(TokenType::LParen)
40056                && prev_token_type != Some(TokenType::Eq)
40057                && prev_token_type != Some(TokenType::Dot)
40058                && t.token_type != TokenType::Comma
40059                && t.token_type != TokenType::RParen
40060                && t.token_type != TokenType::LParen
40061                && t.token_type != TokenType::Eq
40062                && t.token_type != TokenType::Dot;
40063
40064            if needs_space {
40065                result.push(' ');
40066            }
40067
40068            if t.token_type == TokenType::String {
40069                result.push('\'');
40070                result.push_str(&t.text.replace('\'', "''"));
40071                result.push('\'');
40072            } else {
40073                result.push_str(&t.text);
40074            }
40075
40076            prev_token_type = Some(t.token_type);
40077            i += 1;
40078        }
40079        result
40080    }
40081
40082    /// Like tokens_to_sql but also uppercases keyword tokens and adds space after commas
40083    fn tokens_to_sql_uppercased(&self, start: usize, end: usize) -> String {
40084        let mut result = String::new();
40085        let mut prev_token_type: Option<TokenType> = None;
40086        let mut prev_token_text: Option<String> = None;
40087
40088        for t in &self.tokens[start..end] {
40089            // Smart spacing: no space before comma, ), . or after (, .
40090            // Add space before ( only when preceded by a structural keyword or identifier
40091            // (e.g., "PRIMARY KEY (Id)", "CLUSTERED (EmpID)")
40092            // but NOT after data type keywords (e.g., "VARCHAR(100)", "INT(11)")
40093            let is_lparen_after_keyword = t.token_type == TokenType::LParen
40094                && prev_token_type.map_or(false, |p: TokenType| {
40095                    // Only add space for structural SQL keywords, not data type keywords
40096                    match p {
40097                        TokenType::PrimaryKey | TokenType::ForeignKey | TokenType::Unique
40098                        | TokenType::Check | TokenType::Index | TokenType::Key
40099                        | TokenType::Constraint | TokenType::References
40100                        | TokenType::Not | TokenType::Null
40101                        | TokenType::Default | TokenType::Values | TokenType::In
40102                        | TokenType::Exists | TokenType::Select | TokenType::From
40103                        | TokenType::Where | TokenType::Having | TokenType::Using
40104                        | TokenType::On | TokenType::Set | TokenType::Into
40105                        | TokenType::Table | TokenType::View | TokenType::Create
40106                        | TokenType::Insert | TokenType::Update | TokenType::Delete
40107                        | TokenType::Join | TokenType::Left | TokenType::Right
40108                        | TokenType::Inner | TokenType::Outer | TokenType::Full
40109                        | TokenType::Cross | TokenType::Case | TokenType::When
40110                        | TokenType::Then | TokenType::Else | TokenType::End
40111                        | TokenType::If | TokenType::Partition | TokenType::Over
40112                        | TokenType::Between | TokenType::Like | TokenType::Replace
40113                        | TokenType::Grant | TokenType::Revoke
40114                        => true,
40115                        _ => false,
40116                    }
40117                })
40118                // For Var/Identifier tokens, add space before ( only for structural tokens
40119                // (CLUSTERED, NONCLUSTERED, INDEX) but not data types (VARCHAR, INT, etc.)
40120                || (t.token_type == TokenType::LParen
40121                    && prev_token_text.as_ref().map_or(false, |text| {
40122                        let upper = text.to_ascii_uppercase();
40123                        matches!(upper.as_str(),
40124                            "CLUSTERED" | "NONCLUSTERED" | "HASH" | "RANGE"
40125                            | "INCLUDE" | "FILLFACTOR" | "PAD_INDEX"
40126                        )
40127                    }));
40128            let needs_space = !result.is_empty()
40129                && prev_token_type != Some(TokenType::LParen)
40130                && prev_token_type != Some(TokenType::Dot)
40131                && t.token_type != TokenType::Comma
40132                && t.token_type != TokenType::RParen
40133                && t.token_type != TokenType::Dot
40134                && (t.token_type != TokenType::LParen || is_lparen_after_keyword);
40135
40136            // Add space after comma
40137            if prev_token_type == Some(TokenType::Comma) {
40138                result.push(' ');
40139            } else if needs_space {
40140                result.push(' ');
40141            }
40142
40143            if t.token_type == TokenType::String {
40144                // Re-add quotes around string literals
40145                result.push('\'');
40146                result.push_str(&t.text.replace('\'', "''"));
40147                result.push('\'');
40148            } else if t.token_type.is_keyword() {
40149                // Uppercase keyword tokens
40150                result.push_str(&t.text.to_ascii_uppercase());
40151            } else {
40152                // For non-keyword tokens, preserve original text
40153                result.push_str(&t.text);
40154            }
40155
40156            prev_token_type = Some(t.token_type);
40157            prev_token_text = Some(t.text.clone());
40158        }
40159        result
40160    }
40161
40162    /// Check if current token matches type
40163    #[inline]
40164    fn check(&self, token_type: TokenType) -> bool {
40165        if self.is_at_end() {
40166            false
40167        } else {
40168            self.peek().token_type == token_type
40169        }
40170    }
40171
40172    /// Check if current token is a keyword
40173    fn check_keyword(&self) -> bool {
40174        if self.is_at_end() {
40175            false
40176        } else {
40177            self.peek().token_type.is_keyword()
40178        }
40179    }
40180
40181    /// Check if current UNPIVOT token starts an UNPIVOT clause (vs being an alias).
40182    /// UNPIVOT clause starts with: UNPIVOT(, UNPIVOT INCLUDE, or UNPIVOT EXCLUDE
40183    fn is_unpivot_clause_start(&self) -> bool {
40184        if !self.check(TokenType::Unpivot) {
40185            return false;
40186        }
40187        let next_idx = self.current + 1;
40188        if next_idx >= self.tokens.len() {
40189            return false;
40190        }
40191        let next = &self.tokens[next_idx];
40192        if next.token_type == TokenType::LParen {
40193            return true;
40194        }
40195        // UNPIVOT INCLUDE NULLS (...) or UNPIVOT EXCLUDE NULLS (...)
40196        let next_text = next.text.to_ascii_uppercase();
40197        next_text == "INCLUDE" || next_text == "EXCLUDE"
40198    }
40199
40200    /// Check if current token text matches (case-insensitive), does not advance
40201    fn check_keyword_text(&self, keyword: &str) -> bool {
40202        if self.is_at_end() {
40203            false
40204        } else {
40205            self.peek().text.eq_ignore_ascii_case(keyword)
40206        }
40207    }
40208
40209    /// Check if current token is FROM keyword
40210    fn check_from_keyword(&self) -> bool {
40211        self.check(TokenType::From)
40212    }
40213
40214    /// Check if next token matches type
40215    fn check_next(&self, token_type: TokenType) -> bool {
40216        if self.current + 1 >= self.tokens.len() {
40217            false
40218        } else {
40219            self.tokens[self.current + 1].token_type == token_type
40220        }
40221    }
40222
40223    /// Check if next token is an identifier with specific name (case-insensitive)
40224    fn check_next_identifier(&self, name: &str) -> bool {
40225        if self.current + 1 >= self.tokens.len() {
40226            false
40227        } else {
40228            let token = &self.tokens[self.current + 1];
40229            (token.token_type == TokenType::Var || token.token_type == TokenType::Identifier)
40230                && token.text.eq_ignore_ascii_case(name)
40231        }
40232    }
40233
40234    /// Match an identifier with specific text (case insensitive)
40235    /// Checks for Identifier, Var, and QuotedIdentifier tokens
40236    fn match_identifier(&mut self, text: &str) -> bool {
40237        if (self.check(TokenType::Identifier)
40238            || self.check(TokenType::Var)
40239            || self.check(TokenType::QuotedIdentifier))
40240            && self.peek().text.eq_ignore_ascii_case(text)
40241        {
40242            self.skip();
40243            true
40244        } else {
40245            false
40246        }
40247    }
40248
40249    /// Check if current token is an identifier with specific text (case insensitive)
40250    /// Does NOT advance the parser
40251    fn check_identifier(&self, text: &str) -> bool {
40252        if self.is_at_end() {
40253            return false;
40254        }
40255        (self.check(TokenType::Identifier)
40256            || self.check(TokenType::Var)
40257            || self.check(TokenType::QuotedIdentifier))
40258            && self.peek().text.eq_ignore_ascii_case(text)
40259    }
40260
40261    /// Check if current token is a "safe" keyword that can be used as an identifier.
40262    /// Check if the current Percent token is a PERCENT modifier (not a modulo operator).
40263    /// "PERCENT" spelled out is always a modifier. "%" is a modifier when followed by
40264    /// a clause boundary (OFFSET, end of input, semicolon, RParen, comma, etc.)
40265    fn is_percent_modifier(&self) -> bool {
40266        if self.is_at_end() {
40267            return false;
40268        }
40269        if self.peek().text.eq_ignore_ascii_case("PERCENT") {
40270            return true;
40271        }
40272        // "%" symbol — only treat as PERCENT modifier if followed by a boundary
40273        if self.peek().text == "%" {
40274            let next_idx = self.current + 1;
40275            if next_idx >= self.tokens.len() {
40276                return true; // at end — it's PERCENT
40277            }
40278            let next_type = self.tokens[next_idx].token_type;
40279            return matches!(
40280                next_type,
40281                TokenType::Offset
40282                    | TokenType::Semicolon
40283                    | TokenType::RParen
40284                    | TokenType::From
40285                    | TokenType::Where
40286                    | TokenType::GroupBy
40287                    | TokenType::OrderBy
40288                    | TokenType::Having
40289                    | TokenType::Union
40290                    | TokenType::Intersect
40291                    | TokenType::Except
40292                    | TokenType::Comma
40293                    | TokenType::With // WITH TIES
40294            ) || next_idx >= self.tokens.len();
40295        }
40296        false
40297    }
40298
40299    /// Structural keywords like FROM, WHERE, JOIN, SELECT are NOT safe.
40300    /// Non-structural keywords like FILTER, UPDATE, END, VALUES can be used as identifiers.
40301    fn is_safe_keyword_as_identifier(&self) -> bool {
40302        if self.is_at_end() {
40303            return false;
40304        }
40305        let token_type = self.peek().token_type;
40306        // Structural keywords that should NOT be used as identifiers
40307        let is_structural = matches!(
40308            token_type,
40309            TokenType::From
40310                | TokenType::Where
40311                | TokenType::Select
40312                | TokenType::Insert
40313                | TokenType::Delete
40314                | TokenType::Create
40315                | TokenType::Drop
40316                | TokenType::Alter
40317                | TokenType::Join
40318                | TokenType::Inner
40319                | TokenType::Cross
40320                | TokenType::On
40321                | TokenType::GroupBy
40322                | TokenType::OrderBy
40323                | TokenType::Having
40324                | TokenType::With
40325                | TokenType::Union
40326                | TokenType::Intersect
40327                | TokenType::Except
40328                | TokenType::Qualify
40329                | TokenType::Into
40330                | TokenType::Set
40331                | TokenType::Using
40332                | TokenType::Lateral
40333                | TokenType::Natural
40334        );
40335        // ClickHouse allows many SQL keywords as identifiers (table names, column aliases, etc.)
40336        if matches!(
40337            self.config.dialect,
40338            Some(crate::dialects::DialectType::ClickHouse)
40339        ) {
40340            let is_ch_structural = matches!(
40341                token_type,
40342                TokenType::From
40343                    | TokenType::Where
40344                    | TokenType::Select
40345                    | TokenType::Create
40346                    | TokenType::Drop
40347                    | TokenType::Alter
40348                    | TokenType::On
40349                    | TokenType::GroupBy
40350                    | TokenType::OrderBy
40351                    | TokenType::Having
40352                    | TokenType::With
40353                    | TokenType::Union
40354                    | TokenType::Intersect
40355                    | TokenType::Except
40356                    | TokenType::Into
40357                    | TokenType::Using
40358                    | TokenType::Lateral
40359                    | TokenType::Natural
40360            );
40361            // Also allow certain operator tokens and non-keyword tokens as identifiers
40362            if matches!(token_type, TokenType::RLike | TokenType::Values) {
40363                return true;
40364            }
40365            return self.peek().token_type.is_keyword() && !is_ch_structural;
40366        }
40367        // If it's a keyword but NOT structural, it's safe to use as identifier
40368        self.peek().token_type.is_keyword() && !is_structural
40369    }
40370
40371    /// Check if a token at current position is the last meaningful token in an expression context.
40372    /// This is used to detect when a keyword like IS or KEEP should be treated as an alias
40373    /// instead of an operator keyword.
40374    fn is_last_expression_token(&self, _token_type: TokenType) -> bool {
40375        // Check if the token after the current one is end-of-input or a clause boundary
40376        let next_idx = self.current + 1;
40377        if next_idx >= self.tokens.len() {
40378            return true; // at end of input
40379        }
40380        let next_type = self.tokens[next_idx].token_type;
40381        // Clause boundaries that indicate the current token is the last in the expression
40382        matches!(
40383            next_type,
40384            TokenType::From
40385                | TokenType::Where
40386                | TokenType::GroupBy
40387                | TokenType::OrderBy
40388                | TokenType::Having
40389                | TokenType::Limit
40390                | TokenType::Union
40391                | TokenType::Intersect
40392                | TokenType::Except
40393                | TokenType::Semicolon
40394                | TokenType::RParen
40395                | TokenType::Comma
40396        )
40397    }
40398
40399    /// Check if current token is a type keyword (for lambda type annotations)
40400    fn is_type_keyword(&self) -> bool {
40401        if self.is_at_end() {
40402            return false;
40403        }
40404        let token = self.peek();
40405        // Check for common type keywords that might appear in lambda annotations
40406        // Use text comparison to avoid depending on specific TokenType variants
40407        let text_upper = token.text.to_ascii_uppercase();
40408        matches!(
40409            text_upper.as_str(),
40410            "INT"
40411                | "INTEGER"
40412                | "BIGINT"
40413                | "SMALLINT"
40414                | "TINYINT"
40415                | "DOUBLE"
40416                | "FLOAT"
40417                | "DECIMAL"
40418                | "NUMERIC"
40419                | "REAL"
40420                | "VARCHAR"
40421                | "CHAR"
40422                | "TEXT"
40423                | "STRING"
40424                | "NVARCHAR"
40425                | "NCHAR"
40426                | "BOOLEAN"
40427                | "BOOL"
40428                | "DATE"
40429                | "TIME"
40430                | "TIMESTAMP"
40431                | "DATETIME"
40432                | "INTERVAL"
40433                | "BINARY"
40434                | "VARBINARY"
40435                | "BLOB"
40436                | "ARRAY"
40437                | "MAP"
40438                | "STRUCT"
40439                | "OBJECT"
40440                | "VARIANT"
40441                | "JSON"
40442                | "NUMBER"
40443                | "VARCHAR2"
40444        )
40445    }
40446
40447    /// Check if current token is a command keyword that can safely be used as an implicit alias.
40448    /// This is a narrow set of command-like keywords (GET, PUT, COPY, SHOW, etc.) that are
40449    /// unlikely to conflict with SQL clause keywords when used as implicit aliases.
40450    fn is_command_keyword_as_alias(&self) -> bool {
40451        if self.is_at_end() {
40452            return false;
40453        }
40454        let token_type = self.peek().token_type;
40455        // FORMAT is a query modifier in ClickHouse, so don't treat it as an alias there
40456        if matches!(token_type, TokenType::Format) {
40457            return !matches!(
40458                self.config.dialect,
40459                Some(crate::dialects::DialectType::ClickHouse)
40460            );
40461        }
40462        // Base keywords that can be aliases in all dialects
40463        if matches!(
40464            token_type,
40465            TokenType::Get
40466                | TokenType::Put
40467                | TokenType::Copy
40468                | TokenType::Show
40469                | TokenType::Rename
40470                | TokenType::Enum
40471                | TokenType::Sample
40472                | TokenType::Collate
40473                | TokenType::Add
40474        ) {
40475            return true;
40476        }
40477        // Spark/Hive allow LIMIT and OFFSET as aliases (without quoting),
40478        // but only when NOT followed by a number/expression (which means it's the actual clause)
40479        if matches!(
40480            self.config.dialect,
40481            Some(crate::dialects::DialectType::Spark)
40482                | Some(crate::dialects::DialectType::Hive)
40483                | Some(crate::dialects::DialectType::Databricks)
40484        ) && matches!(token_type, TokenType::Limit | TokenType::Offset)
40485        {
40486            let next = self.current + 1;
40487            let next_is_value = next < self.tokens.len()
40488                && matches!(
40489                    self.tokens[next].token_type,
40490                    TokenType::Number
40491                        | TokenType::LParen
40492                        | TokenType::Var
40493                        | TokenType::Parameter
40494                        | TokenType::All
40495                );
40496            if !next_is_value {
40497                return true;
40498            }
40499        }
40500        false
40501    }
40502
40503    /// Check if current token is a keyword that can be used as a table alias.
40504    /// This is more permissive than is_safe_keyword_as_identifier - it allows
40505    /// LEFT, RIGHT, OUTER, FULL which are JOIN keywords but can also be aliases.
40506    fn can_be_alias_keyword(&self) -> bool {
40507        if self.is_at_end() {
40508            return false;
40509        }
40510        let token_type = self.peek().token_type;
40511        // Keywords that can be used as aliases (similar to is_safe_keyword but more permissive)
40512        matches!(
40513            token_type,
40514            TokenType::Left
40515                | TokenType::Right
40516                | TokenType::Outer
40517                | TokenType::Full
40518                | TokenType::Only
40519                | TokenType::Next
40520                | TokenType::All
40521                | TokenType::If
40522        ) || self.is_safe_keyword_as_identifier()
40523    }
40524
40525    /// Match and consume a token type
40526    fn match_token(&mut self, token_type: TokenType) -> bool {
40527        if self.check(token_type) {
40528            self.skip();
40529            true
40530        } else {
40531            false
40532        }
40533    }
40534
40535    /// Match a sequence of keywords
40536    fn match_keywords(&mut self, keywords: &[TokenType]) -> bool {
40537        // Check if all keywords match
40538        for (i, &kw) in keywords.iter().enumerate() {
40539            if self.current + i >= self.tokens.len() {
40540                return false;
40541            }
40542            if self.tokens[self.current + i].token_type != kw {
40543                return false;
40544            }
40545        }
40546
40547        // Consume all matched keywords
40548        self.current += keywords.len();
40549        true
40550    }
40551
40552    /// Expect a specific token type
40553    fn expect(&mut self, token_type: TokenType) -> Result<Token> {
40554        if self.check(token_type) {
40555            Ok(self.advance())
40556        } else {
40557            let got = if self.is_at_end() {
40558                "end of input".to_string()
40559            } else {
40560                format!("{:?}", self.peek().token_type)
40561            };
40562            let got_text = if self.is_at_end() {
40563                "".to_string()
40564            } else {
40565                self.peek().text.clone()
40566            };
40567            let start = self.current.saturating_sub(3);
40568            let end = (self.current + 4).min(self.tokens.len());
40569            let context = self.tokens_to_sql(start, end).replace('\n', " ");
40570            Err(self.parse_error(format!(
40571                "Expected {:?}, got {} ('{}') near [{}]",
40572                token_type, got, got_text, context
40573            )))
40574        }
40575    }
40576
40577    /// Expect a `>` token, handling the case where `>>` was tokenized as GtGt
40578    /// This is needed for parsing nested generic types like `ARRAY<ARRAY<INT>>`
40579    fn expect_gt(&mut self) -> Result<Token> {
40580        if self.check(TokenType::Gt) {
40581            Ok(self.advance())
40582        } else if self.check(TokenType::GtGt) {
40583            // Split >> into two > tokens
40584            // Replace the GtGt with Gt and return a synthetic Gt token
40585            let token = self.peek().clone();
40586            self.tokens[self.current] = Token {
40587                token_type: TokenType::Gt,
40588                text: ">".to_string(),
40589                span: Span {
40590                    start: token.span.start + 1,
40591                    end: token.span.end,
40592                    line: token.span.line,
40593                    column: token.span.column + 1,
40594                },
40595                comments: Vec::new(),
40596                trailing_comments: Vec::new(),
40597            };
40598            Ok(Token {
40599                token_type: TokenType::Gt,
40600                text: ">".to_string(),
40601                span: Span {
40602                    start: token.span.start,
40603                    end: token.span.start + 1,
40604                    line: token.span.line,
40605                    column: token.span.column,
40606                },
40607                comments: token.comments,
40608                trailing_comments: Vec::new(),
40609            })
40610        } else {
40611            Err(self.parse_error(format!(
40612                "Expected Gt, got {:?}",
40613                if self.is_at_end() {
40614                    "end of input".to_string()
40615                } else {
40616                    format!("{:?}", self.peek().token_type)
40617                }
40618            )))
40619        }
40620    }
40621
40622    /// Expect a string literal and return its value
40623    fn expect_string(&mut self) -> Result<String> {
40624        if self.check(TokenType::String) || self.check(TokenType::DollarString) {
40625            Ok(self.advance().text)
40626        } else {
40627            Err(self.parse_error(format!(
40628                "Expected string, got {:?}",
40629                if self.is_at_end() {
40630                    "end of input".to_string()
40631                } else {
40632                    format!("{:?}", self.peek().token_type)
40633                }
40634            )))
40635        }
40636    }
40637
40638    /// Check if the current token is any kind of identifier (regular, quoted, or var)
40639    fn is_identifier_token(&self) -> bool {
40640        self.check(TokenType::Var)
40641            || self.check(TokenType::Identifier)
40642            || self.check(TokenType::QuotedIdentifier)
40643    }
40644
40645    /// Check if current token is a stage reference (starts with @)
40646    /// This handles both DAt token and Var tokens that start with @
40647    fn is_stage_reference(&self) -> bool {
40648        self.check(TokenType::DAt)
40649            || (self.check(TokenType::Var) && self.peek().text.starts_with('@'))
40650    }
40651
40652    /// Check if the current token could be a MySQL numeric-starting identifier (e.g., 00f, 1d)
40653    /// This checks that the Number token is followed by a connected Var/Identifier token
40654    fn is_mysql_numeric_identifier(&self) -> bool {
40655        if !self.check(TokenType::Number)
40656            || !matches!(
40657                self.config.dialect,
40658                Some(crate::dialects::DialectType::MySQL)
40659            )
40660        {
40661            return false;
40662        }
40663        // Check if the next token is connected (no space) and is a var/identifier
40664        if self.current + 1 < self.tokens.len() {
40665            let curr = &self.tokens[self.current];
40666            let next = &self.tokens[self.current + 1];
40667            // Tokens are connected if they are immediately adjacent (no whitespace between)
40668            // span.end is exclusive, so if curr.end == next.start, they are adjacent
40669            let connected = curr.span.end == next.span.start;
40670            connected
40671                && (next.token_type == TokenType::Var || next.token_type == TokenType::Identifier)
40672        } else {
40673            false
40674        }
40675    }
40676
40677    /// Parse a MySQL numeric-starting identifier (e.g., 00f, 1d)
40678    /// Merges the number token with connected identifier tokens
40679    fn parse_mysql_numeric_identifier(&mut self) -> Identifier {
40680        let num_token = self.advance();
40681        let mut name = num_token.text.clone();
40682        // Merge with connected identifier/var tokens
40683        while !self.is_at_end()
40684            && self.is_connected()
40685            && (self.check(TokenType::Var) || self.check(TokenType::Identifier))
40686        {
40687            let tok = self.advance();
40688            name.push_str(&tok.text);
40689        }
40690        Identifier {
40691            name,
40692            // sqlglot treats this as an identifier token and re-emits it quoted.
40693            quoted: true,
40694            trailing_comments: Vec::new(),
40695            span: None,
40696        }
40697    }
40698
40699    /// Check if an uppercase string starting with '_' is a MySQL charset introducer
40700    fn is_mysql_charset_introducer(text: &str) -> bool {
40701        matches!(
40702            text,
40703            "_ARMSCII8"
40704                | "_ASCII"
40705                | "_BIG5"
40706                | "_BINARY"
40707                | "_CP1250"
40708                | "_CP1251"
40709                | "_CP1256"
40710                | "_CP1257"
40711                | "_CP850"
40712                | "_CP852"
40713                | "_CP866"
40714                | "_CP932"
40715                | "_DEC8"
40716                | "_EUCJPMS"
40717                | "_EUCKR"
40718                | "_GB18030"
40719                | "_GB2312"
40720                | "_GBK"
40721                | "_GEOSTD8"
40722                | "_GREEK"
40723                | "_HEBREW"
40724                | "_HP8"
40725                | "_KEYBCS2"
40726                | "_KOI8R"
40727                | "_KOI8U"
40728                | "_LATIN1"
40729                | "_LATIN2"
40730                | "_LATIN5"
40731                | "_LATIN7"
40732                | "_MACCE"
40733                | "_MACROMAN"
40734                | "_SJIS"
40735                | "_SWE7"
40736                | "_TIS620"
40737                | "_UCS2"
40738                | "_UJIS"
40739                | "_UTF8"
40740                | "_UTF16"
40741                | "_UTF16LE"
40742                | "_UTF32"
40743                | "_UTF8MB3"
40744                | "_UTF8MB4"
40745        )
40746    }
40747
40748    /// Check if the current token can be used as an identifier (includes keywords)
40749    fn is_identifier_or_keyword_token(&self) -> bool {
40750        self.is_identifier_token() || self.check_keyword()
40751    }
40752
40753    /// Expect an identifier and return an Identifier struct with quoted flag
40754    fn expect_identifier_with_quoted(&mut self) -> Result<Identifier> {
40755        if self.is_mysql_numeric_identifier() {
40756            return Ok(self.parse_mysql_numeric_identifier());
40757        }
40758        if self.is_identifier_token() {
40759            let token = self.advance();
40760            let quoted = token.token_type == TokenType::QuotedIdentifier;
40761            Ok(Identifier {
40762                name: token.text,
40763                quoted,
40764                trailing_comments: Vec::new(),
40765                span: None,
40766            })
40767        } else if self.check(TokenType::LBrace)
40768            && matches!(
40769                self.config.dialect,
40770                Some(crate::dialects::DialectType::ClickHouse)
40771            )
40772        {
40773            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40774                if let Expression::Parameter(param) = &param_expr {
40775                    let name = format!(
40776                        "{{{}: {}}}",
40777                        param.name.as_deref().unwrap_or(""),
40778                        param.expression.as_deref().unwrap_or("")
40779                    );
40780                    return Ok(Identifier {
40781                        name,
40782                        quoted: false,
40783                        trailing_comments: Vec::new(),
40784                        span: None,
40785                    });
40786                }
40787            }
40788            Err(self.parse_error("Expected identifier, got LBrace"))
40789        } else {
40790            Err(self.parse_error(format!(
40791                "Expected identifier, got {:?}",
40792                if self.is_at_end() {
40793                    "end of input".to_string()
40794                } else {
40795                    format!("{:?}", self.peek().token_type)
40796                }
40797            )))
40798        }
40799    }
40800
40801    /// Parse a possibly dot-qualified identifier into parts (e.g. "mydb.hr" → [mydb, hr]).
40802    fn parse_identifier_parts(&mut self) -> Result<Vec<Identifier>> {
40803        let first = self.expect_identifier_with_quoted()?;
40804        let mut parts = vec![first];
40805        while self.match_token(TokenType::Dot) {
40806            parts.push(self.expect_identifier_with_quoted()?);
40807        }
40808        Ok(parts)
40809    }
40810
40811    /// Expect an identifier or keyword (for column names, field names, etc.)
40812    fn expect_identifier_or_keyword_with_quoted(&mut self) -> Result<Identifier> {
40813        // MySQL numeric-starting identifiers (e.g., 00f, 1d)
40814        if self.is_mysql_numeric_identifier() {
40815            return Ok(self.parse_mysql_numeric_identifier());
40816        }
40817        // Also accept ? (Parameter) as an identifier placeholder
40818        // For positional parameters like $23, the token text is "23" (without $)
40819        if self.check(TokenType::Parameter) {
40820            let token = self.advance();
40821            // If the text is a number, it's a positional parameter like $1, $2, $23
40822            // Construct $N as the identifier name
40823            let name = if token.text.chars().all(|c| c.is_ascii_digit()) && !token.text.is_empty() {
40824                format!("${}", token.text)
40825            } else {
40826                // Plain ? placeholder or other parameter
40827                "?".to_string()
40828            };
40829            return Ok(Identifier {
40830                name,
40831                quoted: false,
40832                trailing_comments: Vec::new(),
40833                span: None,
40834            });
40835        }
40836        if self.is_identifier_or_keyword_token() {
40837            let token = self.advance();
40838            let quoted = token.token_type == TokenType::QuotedIdentifier;
40839            Ok(Identifier {
40840                name: token.text,
40841                quoted,
40842                trailing_comments: Vec::new(),
40843                span: None,
40844            })
40845        } else if self.check(TokenType::LBrace)
40846            && matches!(
40847                self.config.dialect,
40848                Some(crate::dialects::DialectType::ClickHouse)
40849            )
40850        {
40851            // ClickHouse query parameter: {name:Type}
40852            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40853                // Extract the parameter name to use as the identifier
40854                if let Expression::Parameter(param) = &param_expr {
40855                    let name = format!(
40856                        "{{{}: {}}}",
40857                        param.name.as_deref().unwrap_or(""),
40858                        param.expression.as_deref().unwrap_or("")
40859                    );
40860                    return Ok(Identifier {
40861                        name,
40862                        quoted: false,
40863                        trailing_comments: Vec::new(),
40864                        span: None,
40865                    });
40866                }
40867            }
40868            Err(self.parse_error("Expected identifier, got LBrace"))
40869        } else {
40870            Err(self.parse_error(format!(
40871                "Expected identifier, got {:?}",
40872                if self.is_at_end() {
40873                    "end of input".to_string()
40874                } else {
40875                    format!("{:?}", self.peek().token_type)
40876                }
40877            )))
40878        }
40879    }
40880
40881    /// Expect an identifier
40882    fn expect_identifier(&mut self) -> Result<String> {
40883        if self.is_identifier_token() {
40884            Ok(self.advance().text)
40885        } else if self.check(TokenType::LBrace)
40886            && matches!(
40887                self.config.dialect,
40888                Some(crate::dialects::DialectType::ClickHouse)
40889            )
40890        {
40891            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40892                if let Expression::Parameter(param) = &param_expr {
40893                    return Ok(format!(
40894                        "{{{}: {}}}",
40895                        param.name.as_deref().unwrap_or(""),
40896                        param.expression.as_deref().unwrap_or("")
40897                    ));
40898                }
40899            }
40900            Err(self.parse_error("Expected identifier, got LBrace"))
40901        } else {
40902            Err(self.parse_error(format!(
40903                "Expected identifier, got {:?}",
40904                if self.is_at_end() {
40905                    "end of input".to_string()
40906                } else {
40907                    format!("{:?}", self.peek().token_type)
40908                }
40909            )))
40910        }
40911    }
40912
40913    /// Expect an identifier or keyword (for aliases, column names, etc.)
40914    fn expect_identifier_or_keyword(&mut self) -> Result<String> {
40915        if self.is_identifier_or_keyword_token() {
40916            Ok(self.advance().text)
40917        } else if self.check(TokenType::LBrace)
40918            && matches!(
40919                self.config.dialect,
40920                Some(crate::dialects::DialectType::ClickHouse)
40921            )
40922        {
40923            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40924                if let Expression::Parameter(param) = &param_expr {
40925                    return Ok(format!(
40926                        "{{{}: {}}}",
40927                        param.name.as_deref().unwrap_or(""),
40928                        param.expression.as_deref().unwrap_or("")
40929                    ));
40930                }
40931            }
40932            Err(self.parse_error("Expected identifier, got LBrace"))
40933        } else {
40934            Err(self.parse_error(format!(
40935                "Expected identifier, got {:?}",
40936                if self.is_at_end() {
40937                    "end of input".to_string()
40938                } else {
40939                    format!("{:?}", self.peek().token_type)
40940                }
40941            )))
40942        }
40943    }
40944
40945    /// Expect an identifier or safe keyword (for CTE names, column names in CREATE TABLE, etc.)
40946    /// This is more permissive than expect_identifier but excludes structural keywords
40947    fn expect_identifier_or_safe_keyword(&mut self) -> Result<String> {
40948        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
40949            Ok(self.advance().text)
40950        } else if self.check(TokenType::LBrace)
40951            && matches!(
40952                self.config.dialect,
40953                Some(crate::dialects::DialectType::ClickHouse)
40954            )
40955        {
40956            if let Some(param_expr) = self.parse_clickhouse_braced_parameter()? {
40957                if let Expression::Parameter(param) = &param_expr {
40958                    return Ok(format!(
40959                        "{{{}: {}}}",
40960                        param.name.as_deref().unwrap_or(""),
40961                        param.expression.as_deref().unwrap_or("")
40962                    ));
40963                }
40964            }
40965            Err(self.parse_error("Expected identifier, got LBrace"))
40966        } else {
40967            Err(self.parse_error(format!(
40968                "Expected identifier, got {:?}",
40969                if self.is_at_end() {
40970                    "end of input".to_string()
40971                } else {
40972                    format!("{:?}", self.peek().token_type)
40973                }
40974            )))
40975        }
40976    }
40977
40978    /// Expect an identifier or safe keyword, preserving quoted flag
40979    fn expect_identifier_or_safe_keyword_with_quoted(&mut self) -> Result<Identifier> {
40980        if self.is_mysql_numeric_identifier() {
40981            return Ok(self.parse_mysql_numeric_identifier());
40982        }
40983        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
40984            let token = self.advance();
40985            let quoted = token.token_type == TokenType::QuotedIdentifier;
40986            Ok(Identifier {
40987                name: token.text,
40988                quoted,
40989                trailing_comments: Vec::new(),
40990                span: None,
40991            })
40992        } else {
40993            Err(self.parse_error(format!(
40994                "Expected identifier, got {:?}",
40995                if self.is_at_end() {
40996                    "end of input".to_string()
40997                } else {
40998                    format!("{:?}", self.peek().token_type)
40999                }
41000            )))
41001        }
41002    }
41003
41004    fn expect_identifier_or_alias_keyword_with_quoted(&mut self) -> Result<Identifier> {
41005        // ClickHouse: any keyword can be used as a table alias after explicit AS
41006        let ch_keyword = matches!(
41007            self.config.dialect,
41008            Some(crate::dialects::DialectType::ClickHouse)
41009        ) && self.peek().token_type.is_keyword();
41010        if self.is_identifier_token()
41011            || self.can_be_alias_keyword()
41012            || self.is_safe_keyword_as_identifier()
41013            || ch_keyword
41014        {
41015            let token = self.advance();
41016            let quoted = token.token_type == TokenType::QuotedIdentifier;
41017            Ok(Identifier {
41018                name: token.text,
41019                quoted,
41020                trailing_comments: Vec::new(),
41021                span: None,
41022            })
41023        } else if self.check(TokenType::String)
41024            && matches!(
41025                self.config.dialect,
41026                Some(crate::dialects::DialectType::DuckDB)
41027            )
41028        {
41029            // DuckDB allows string literals as identifiers (e.g., WITH 'x' AS (...))
41030            let token = self.advance();
41031            Ok(Identifier {
41032                name: token.text,
41033                quoted: true,
41034                trailing_comments: Vec::new(),
41035                span: None,
41036            })
41037        } else {
41038            Err(self.parse_error(format!(
41039                "Expected identifier, got {:?}",
41040                if self.is_at_end() {
41041                    "end of input".to_string()
41042                } else {
41043                    format!("{:?}", self.peek().token_type)
41044                }
41045            )))
41046        }
41047    }
41048
41049    /// Expect a number
41050    fn expect_number(&mut self) -> Result<i64> {
41051        let negative = self.match_token(TokenType::Dash);
41052        if self.check(TokenType::Number) {
41053            let text = self.advance().text;
41054            let val = text
41055                .parse::<i64>()
41056                .map_err(|_| self.parse_error(format!("Invalid number: {}", text)))?;
41057            Ok(if negative { -val } else { val })
41058        } else {
41059            Err(self.parse_error("Expected number"))
41060        }
41061    }
41062
41063    /// Parse a comma-separated list of expressions.
41064    /// Supports named arguments with => or := syntax.
41065    fn parse_expression_list_with_capacity(
41066        &mut self,
41067        capacity_hint: usize,
41068    ) -> Result<Vec<Expression>> {
41069        let mut expressions = Vec::with_capacity(capacity_hint);
41070
41071        loop {
41072            // Check if this is a named argument: identifier => value or identifier := value
41073            // Also check for safe keywords (like TYPE, FORMAT, etc.) that can be used as named arg names
41074            let expr = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
41075                let start_pos = self.current;
41076                let name = self.expect_identifier_or_keyword_with_quoted()?;
41077
41078                if self.match_token(TokenType::FArrow) {
41079                    // name => value
41080                    let value = self.parse_expression()?;
41081                    Expression::NamedArgument(Box::new(NamedArgument {
41082                        name,
41083                        value,
41084                        separator: NamedArgSeparator::DArrow,
41085                    }))
41086                } else if self.match_token(TokenType::ColonEq) {
41087                    // name := value
41088                    let value = self.parse_expression()?;
41089                    Expression::NamedArgument(Box::new(NamedArgument {
41090                        name,
41091                        value,
41092                        separator: NamedArgSeparator::ColonEq,
41093                    }))
41094                } else {
41095                    // Not a named argument, backtrack and parse as regular expression
41096                    self.current = start_pos;
41097                    self.parse_expression()?
41098                }
41099            } else {
41100                self.parse_expression()?
41101            };
41102
41103            // Check for AS alias on this expression (Spark/Hive: IF(cond, val AS name, ...))
41104            let expr = if self.check(TokenType::As) {
41105                let as_pos = self.current;
41106                self.skip(); // consume AS
41107                             // Check if what follows looks like an alias name
41108                if self.is_identifier_token()
41109                    || self.is_safe_keyword_as_identifier()
41110                    || (matches!(
41111                        self.config.dialect,
41112                        Some(crate::dialects::DialectType::ClickHouse)
41113                    ) && self.peek().token_type.is_keyword())
41114                {
41115                    let alias = self.expect_identifier_or_keyword_with_quoted()?;
41116                    let alias_expr = Expression::Alias(Box::new(Alias {
41117                        this: expr,
41118                        alias,
41119                        column_aliases: Vec::new(),
41120                        pre_alias_comments: Vec::new(),
41121                        trailing_comments: Vec::new(),
41122                        inferred_type: None,
41123                    }));
41124                    // ClickHouse: if followed by an operator, the alias is part of a bigger expression
41125                    // e.g., blockSize() AS bs < 1000 means (blockSize() AS bs) < 1000
41126                    if matches!(
41127                        self.config.dialect,
41128                        Some(crate::dialects::DialectType::ClickHouse)
41129                    ) && matches!(
41130                        self.peek().token_type,
41131                        TokenType::Lt
41132                            | TokenType::Gt
41133                            | TokenType::Lte
41134                            | TokenType::Gte
41135                            | TokenType::Eq
41136                            | TokenType::Neq
41137                            | TokenType::Plus
41138                            | TokenType::Dash
41139                            | TokenType::Star
41140                            | TokenType::Slash
41141                            | TokenType::Percent
41142                            | TokenType::And
41143                            | TokenType::Or
41144                            | TokenType::Like
41145                            | TokenType::Not
41146                            | TokenType::In
41147                            | TokenType::Is
41148                            | TokenType::Between
41149                    ) {
41150                        // Parse the operator and right-hand side
41151                        let op_token = self.advance();
41152                        let right = self.parse_expression()?;
41153                        match op_token.token_type {
41154                            TokenType::Lt => {
41155                                Expression::Lt(Box::new(BinaryOp::new(alias_expr, right)))
41156                            }
41157                            TokenType::Gt => {
41158                                Expression::Gt(Box::new(BinaryOp::new(alias_expr, right)))
41159                            }
41160                            TokenType::Lte => {
41161                                Expression::Lte(Box::new(BinaryOp::new(alias_expr, right)))
41162                            }
41163                            TokenType::Gte => {
41164                                Expression::Gte(Box::new(BinaryOp::new(alias_expr, right)))
41165                            }
41166                            TokenType::Eq => {
41167                                Expression::Eq(Box::new(BinaryOp::new(alias_expr, right)))
41168                            }
41169                            TokenType::Neq => {
41170                                Expression::Neq(Box::new(BinaryOp::new(alias_expr, right)))
41171                            }
41172                            TokenType::Plus => {
41173                                Expression::Add(Box::new(BinaryOp::new(alias_expr, right)))
41174                            }
41175                            TokenType::Dash => {
41176                                Expression::Sub(Box::new(BinaryOp::new(alias_expr, right)))
41177                            }
41178                            TokenType::Star => {
41179                                Expression::Mul(Box::new(BinaryOp::new(alias_expr, right)))
41180                            }
41181                            TokenType::Slash => {
41182                                Expression::Div(Box::new(BinaryOp::new(alias_expr, right)))
41183                            }
41184                            TokenType::Percent => {
41185                                Expression::Mod(Box::new(BinaryOp::new(alias_expr, right)))
41186                            }
41187                            TokenType::And => {
41188                                Expression::And(Box::new(BinaryOp::new(alias_expr, right)))
41189                            }
41190                            TokenType::Or => {
41191                                Expression::Or(Box::new(BinaryOp::new(alias_expr, right)))
41192                            }
41193                            _ => alias_expr, // fallback, shouldn't happen
41194                        }
41195                    } else {
41196                        alias_expr
41197                    }
41198                } else {
41199                    // Not an alias name, backtrack
41200                    self.current = as_pos;
41201                    expr
41202                }
41203            } else {
41204                expr
41205            };
41206
41207            // Check for trailing comments on this expression
41208            // Only wrap in Annotated for expression types that don't have their own trailing_comments field
41209            let trailing_comments = self.previous_trailing_comments().to_vec();
41210            let expr = if trailing_comments.is_empty() {
41211                expr
41212            } else {
41213                // Only annotate Literals and other types that don't capture trailing comments
41214                match &expr {
41215                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
41216                        Expression::Annotated(Box::new(Annotated {
41217                            this: expr,
41218                            trailing_comments,
41219                        }))
41220                    }
41221                    // For expressions that already capture trailing_comments, don't double-wrap
41222                    _ => expr,
41223                }
41224            };
41225            expressions.push(expr);
41226
41227            if !self.match_token(TokenType::Comma) {
41228                break;
41229            }
41230            // ClickHouse: allow trailing comma before RParen in expression lists
41231            if matches!(
41232                self.config.dialect,
41233                Some(crate::dialects::DialectType::ClickHouse)
41234            ) && self.check(TokenType::RParen)
41235            {
41236                break;
41237            }
41238        }
41239
41240        Ok(expressions)
41241    }
41242
41243    /// Parse a comma-separated list of expressions.
41244    /// Supports named arguments with => or := syntax.
41245    fn parse_expression_list(&mut self) -> Result<Vec<Expression>> {
41246        self.parse_expression_list_with_capacity(0)
41247    }
41248
41249    /// Estimate top-level expression count until the next unmatched `)`.
41250    ///
41251    /// This is used for pre-allocating comma-separated lists like `IN (...)`
41252    /// to reduce `Vec` growth churn on very large lists.
41253    fn estimate_expression_list_capacity_until_rparen(&self) -> usize {
41254        if self.current >= self.tokens.len() || self.check(TokenType::RParen) {
41255            return 0;
41256        }
41257
41258        let mut idx = self.current;
41259        let mut paren_depth = 0usize;
41260        let mut bracket_depth = 0usize;
41261        let mut brace_depth = 0usize;
41262        let mut commas = 0usize;
41263        let mut has_any_token = false;
41264
41265        while idx < self.tokens.len() {
41266            let token_type = self.tokens[idx].token_type;
41267            match token_type {
41268                TokenType::LParen => paren_depth += 1,
41269                TokenType::RParen => {
41270                    if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 {
41271                        break;
41272                    }
41273                    paren_depth = paren_depth.saturating_sub(1);
41274                }
41275                TokenType::LBracket => bracket_depth += 1,
41276                TokenType::RBracket => bracket_depth = bracket_depth.saturating_sub(1),
41277                TokenType::LBrace => brace_depth += 1,
41278                TokenType::RBrace => brace_depth = brace_depth.saturating_sub(1),
41279                TokenType::Comma if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 => {
41280                    commas += 1;
41281                }
41282                _ => {}
41283            }
41284            has_any_token = true;
41285            idx += 1;
41286        }
41287
41288        if has_any_token {
41289            commas + 1
41290        } else {
41291            0
41292        }
41293    }
41294
41295    /// Parse function arguments with lambda support (for TRANSFORM and similar functions).
41296    /// Handles Snowflake typed lambda syntax: `a int -> a + 1`
41297    fn parse_function_args_with_lambda(&mut self) -> Result<Vec<Expression>> {
41298        let mut expressions = Vec::new();
41299
41300        loop {
41301            // Try to detect typed lambda: identifier type -> body
41302            let expr = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
41303                let saved_pos = self.current;
41304                let ident_token = self.advance();
41305                let ident_name = ident_token.text.clone();
41306
41307                // Check for arrow (simple lambda: a -> body)
41308                if self.match_token(TokenType::Arrow) {
41309                    let body = self.parse_expression()?;
41310                    Expression::Lambda(Box::new(LambdaExpr {
41311                        parameters: vec![Identifier::new(ident_name)],
41312                        body,
41313                        colon: false,
41314                        parameter_types: Vec::new(),
41315                    }))
41316                }
41317                // Check for type annotation followed by arrow: a int -> body
41318                else if !self.is_at_end()
41319                    && self.is_type_keyword()
41320                    && !self.check(TokenType::FArrow)
41321                    && !self.check(TokenType::ColonEq)
41322                {
41323                    let type_annotation = self.parse_data_type()?;
41324                    if self.match_token(TokenType::Arrow) {
41325                        let body = self.parse_expression()?;
41326                        Expression::Lambda(Box::new(LambdaExpr {
41327                            parameters: vec![Identifier::new(ident_name)],
41328                            body,
41329                            colon: false,
41330                            parameter_types: vec![Some(type_annotation)],
41331                        }))
41332                    } else {
41333                        self.current = saved_pos;
41334                        self.parse_expression()?
41335                    }
41336                } else {
41337                    // Not a lambda, backtrack and parse as regular expression
41338                    self.current = saved_pos;
41339                    self.parse_expression()?
41340                }
41341            } else {
41342                self.parse_expression()?
41343            };
41344
41345            expressions.push(expr);
41346            if !self.match_token(TokenType::Comma) {
41347                break;
41348            }
41349        }
41350
41351        Ok(expressions)
41352    }
41353
41354    /// Parse a comma-separated list of expressions for VALUES tuples
41355    /// This variant supports AS aliases on each element (Hive syntax): VALUES (1 AS a, 2 AS b, 3)
41356    fn parse_values_expression_list(&mut self) -> Result<Vec<Expression>> {
41357        let mut expressions = Vec::new();
41358
41359        loop {
41360            // Handle DEFAULT keyword in VALUES - output as unquoted Var (like Python sqlglot's exp.var("DEFAULT"))
41361            let expr = if self.match_token(TokenType::Default) {
41362                Expression::Var(Box::new(crate::expressions::Var {
41363                    this: "DEFAULT".to_string(),
41364                }))
41365            } else {
41366                self.parse_expression()?
41367            };
41368
41369            // Capture trailing comments on the expression (e.g., `1 /* c4 */`)
41370            let trailing_comments = self.previous_trailing_comments().to_vec();
41371            let expr = if !trailing_comments.is_empty() {
41372                match &expr {
41373                    Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => {
41374                        Expression::Annotated(Box::new(crate::expressions::Annotated {
41375                            this: expr,
41376                            trailing_comments,
41377                        }))
41378                    }
41379                    _ => expr,
41380                }
41381            } else {
41382                expr
41383            };
41384
41385            // Check for AS alias on this value element (Hive syntax)
41386            let expr_with_alias = if self.match_token(TokenType::As) {
41387                let alias = self.expect_identifier_or_keyword_with_quoted()?;
41388                Expression::Alias(Box::new(Alias::new(expr, alias)))
41389            } else {
41390                expr
41391            };
41392
41393            expressions.push(expr_with_alias);
41394
41395            if !self.match_token(TokenType::Comma) {
41396                break;
41397            }
41398            // ClickHouse: trailing comma in VALUES, e.g., (1, 2, 3,)
41399            if self.check(TokenType::RParen) {
41400                break;
41401            }
41402        }
41403
41404        Ok(expressions)
41405    }
41406
41407    /// Parse a comma-separated list of identifiers
41408    fn parse_identifier_list(&mut self) -> Result<Vec<Identifier>> {
41409        let mut identifiers = Vec::new();
41410
41411        loop {
41412            // Allow keywords as identifiers in identifier lists (e.g., CTE column aliases)
41413            // Check if it's a quoted identifier before consuming
41414            let quoted = self.check(TokenType::QuotedIdentifier);
41415            let mut name = self.expect_identifier_or_safe_keyword()?;
41416            // ClickHouse: handle dotted names in identifier lists (e.g., INSERT INTO t (n.a, n.b))
41417            // Use keyword_with_quoted to allow any keyword after dot (e.g., replace.from)
41418            if matches!(
41419                self.config.dialect,
41420                Some(crate::dialects::DialectType::ClickHouse)
41421            ) {
41422                while self.match_token(TokenType::Dot) {
41423                    let sub_id = self.expect_identifier_or_keyword_with_quoted()?;
41424                    name = format!("{}.{}", name, sub_id.name);
41425                }
41426            }
41427            let trailing_comments = self.previous_trailing_comments().to_vec();
41428            identifiers.push(Identifier {
41429                name,
41430                quoted,
41431                trailing_comments,
41432                span: None,
41433            });
41434
41435            if !self.match_token(TokenType::Comma) {
41436                break;
41437            }
41438            // ClickHouse: allow trailing comma before RParen in identifier lists
41439            if matches!(
41440                self.config.dialect,
41441                Some(crate::dialects::DialectType::ClickHouse)
41442            ) && self.check(TokenType::RParen)
41443            {
41444                break;
41445            }
41446        }
41447
41448        Ok(identifiers)
41449    }
41450
41451    /// Parse a comma-separated list of column references for USING clause
41452    /// Supports qualified names like table.col but extracts only the column part
41453    fn parse_using_column_list(&mut self) -> Result<Vec<Identifier>> {
41454        let mut identifiers = Vec::new();
41455
41456        loop {
41457            // ClickHouse: USING * — wildcard in USING clause
41458            if matches!(
41459                self.config.dialect,
41460                Some(crate::dialects::DialectType::ClickHouse)
41461            ) && self.match_token(TokenType::Star)
41462            {
41463                identifiers.push(Identifier::new("*".to_string()));
41464                if !self.match_token(TokenType::Comma) {
41465                    break;
41466                }
41467                continue;
41468            }
41469            // Check if it's a quoted identifier before consuming
41470            let quoted = self.check(TokenType::QuotedIdentifier);
41471            let mut name = self.expect_identifier_or_safe_keyword()?;
41472            let mut final_quoted = quoted;
41473
41474            // Handle qualified names: table.column or schema.table.column
41475            // Keep only the final column name
41476            while self.match_token(TokenType::Dot) {
41477                final_quoted = self.check(TokenType::QuotedIdentifier);
41478                name = self.expect_identifier_or_safe_keyword()?;
41479            }
41480
41481            // ClickHouse: USING (col AS alias) — consume optional AS alias
41482            if matches!(
41483                self.config.dialect,
41484                Some(crate::dialects::DialectType::ClickHouse)
41485            ) && self.match_token(TokenType::As)
41486            {
41487                // Use the alias name instead
41488                final_quoted = self.check(TokenType::QuotedIdentifier);
41489                name = self.expect_identifier_or_safe_keyword()?;
41490            }
41491
41492            let trailing_comments = self.previous_trailing_comments().to_vec();
41493            identifiers.push(Identifier {
41494                name,
41495                quoted: final_quoted,
41496                trailing_comments,
41497                span: None,
41498            });
41499
41500            if !self.match_token(TokenType::Comma) {
41501                break;
41502            }
41503        }
41504
41505        Ok(identifiers)
41506    }
41507
41508    /// Parse a comma-separated list of identifiers for index columns.
41509    /// Supports MySQL prefix lengths: col(16) and sort order: col DESC
41510    fn parse_index_identifier_list(&mut self) -> Result<Vec<Identifier>> {
41511        let mut identifiers = Vec::new();
41512
41513        loop {
41514            let quoted = self.check(TokenType::QuotedIdentifier);
41515            let name = self.expect_identifier_or_safe_keyword()?;
41516            let trailing_comments = self.previous_trailing_comments().to_vec();
41517
41518            // Check for prefix length: col(16)
41519            let mut display_name = name.clone();
41520            if self.match_token(TokenType::LParen) {
41521                if self.check(TokenType::Number) {
41522                    let len = self.advance().text;
41523                    display_name = format!("{}({})", name, len);
41524                }
41525                self.expect(TokenType::RParen)?;
41526            }
41527
41528            // Check for DESC/ASC sort order
41529            if self.match_token(TokenType::Desc) {
41530                display_name = format!("{} DESC", display_name);
41531            } else if self.match_token(TokenType::Asc) {
41532                display_name = format!("{} ASC", display_name);
41533            }
41534
41535            identifiers.push(Identifier {
41536                name: display_name,
41537                quoted,
41538                trailing_comments,
41539                span: None,
41540            });
41541
41542            if !self.match_token(TokenType::Comma) {
41543                break;
41544            }
41545        }
41546
41547        Ok(identifiers)
41548    }
41549    // =============================================================================
41550    // Auto-generated Missing Parser Methods
41551    // Total: 296 methods
41552    // =============================================================================
41553
41554    /// parse_add_column - Implemented from Python _parse_add_column
41555    /// Calls: parse_column, parse_column_def_with_exists
41556    #[allow(unused_variables, unused_mut)]
41557    pub fn parse_add_column(&mut self) -> Result<Option<Expression>> {
41558        if self.match_texts(&["FIRST", "AFTER"]) {
41559            // Matched one of: FIRST, AFTER
41560            return Ok(None);
41561        }
41562        Ok(None)
41563    }
41564
41565    /// parse_alias - Parses alias for an expression
41566    /// This method parses just the alias part (AS name or just name)
41567    /// Python: _parse_alias
41568    pub fn parse_alias(&mut self) -> Result<Option<Expression>> {
41569        // Check for AS keyword (explicit alias)
41570        let _explicit = self.match_token(TokenType::Alias);
41571
41572        // Parse the alias identifier
41573        if let Some(alias_expr) = self.parse_id_var()? {
41574            let alias_ident = match alias_expr {
41575                Expression::Identifier(id) => id,
41576                _ => return Ok(None),
41577            };
41578            // Return just the alias identifier wrapped in an expression
41579            return Ok(Some(Expression::Identifier(alias_ident)));
41580        }
41581
41582        Ok(None)
41583    }
41584
41585    /// parse_alias_with_expr - Wraps an expression with an alias if present
41586    pub fn parse_alias_with_expr(
41587        &mut self,
41588        this: Option<Expression>,
41589    ) -> Result<Option<Expression>> {
41590        if this.is_none() {
41591            return Ok(None);
41592        }
41593        let expr = this.unwrap();
41594
41595        // Check for AS keyword (explicit alias)
41596        // Accept both TokenType::Alias and TokenType::As
41597        let has_as = self.match_token(TokenType::Alias) || self.match_token(TokenType::As);
41598
41599        // Check for column aliases: (col1, col2)
41600        if has_as && self.match_token(TokenType::LParen) {
41601            let mut column_aliases = Vec::new();
41602            loop {
41603                if let Some(col_expr) = self.parse_id_var()? {
41604                    if let Expression::Identifier(id) = col_expr {
41605                        column_aliases.push(id);
41606                    }
41607                } else {
41608                    break;
41609                }
41610                if !self.match_token(TokenType::Comma) {
41611                    break;
41612                }
41613            }
41614            self.match_token(TokenType::RParen);
41615
41616            if !column_aliases.is_empty() {
41617                return Ok(Some(Expression::Alias(Box::new(Alias {
41618                    this: expr,
41619                    alias: Identifier::new(String::new()), // Empty alias when only column aliases
41620                    column_aliases,
41621                    pre_alias_comments: Vec::new(),
41622                    trailing_comments: Vec::new(),
41623                    inferred_type: None,
41624                }))));
41625            }
41626        }
41627
41628        // Parse the alias identifier
41629        if let Some(alias_expr) = self.parse_id_var()? {
41630            let alias_ident = match alias_expr {
41631                Expression::Identifier(id) => id,
41632                _ => return Ok(Some(expr)),
41633            };
41634            return Ok(Some(Expression::Alias(Box::new(Alias {
41635                this: expr,
41636                alias: alias_ident,
41637                column_aliases: Vec::new(),
41638                pre_alias_comments: Vec::new(),
41639                trailing_comments: Vec::new(),
41640                inferred_type: None,
41641            }))));
41642        }
41643
41644        Ok(Some(expr))
41645    }
41646
41647    /// parse_alter_diststyle - Implemented from Python _parse_alter_diststyle
41648    #[allow(unused_variables, unused_mut)]
41649    /// parse_alter_diststyle - Parses ALTER TABLE DISTSTYLE clause (Redshift)
41650    /// Python: parser.py:7797-7802
41651    pub fn parse_alter_diststyle(&mut self) -> Result<Option<Expression>> {
41652        // Check for ALL, EVEN, AUTO
41653        if self.match_texts(&["ALL", "EVEN", "AUTO"]) {
41654            let style = self.previous().text.to_ascii_uppercase();
41655            return Ok(Some(Expression::DistStyleProperty(Box::new(
41656                DistStyleProperty {
41657                    this: Box::new(Expression::Identifier(Identifier::new(style))),
41658                },
41659            ))));
41660        }
41661
41662        // KEY DISTKEY column
41663        if self.match_text_seq(&["KEY", "DISTKEY"]) {
41664            if let Some(column) = self.parse_column()? {
41665                return Ok(Some(Expression::DistStyleProperty(Box::new(
41666                    DistStyleProperty {
41667                        this: Box::new(column),
41668                    },
41669                ))));
41670            }
41671        }
41672
41673        Ok(None)
41674    }
41675
41676    /// parse_alter_session - Parses ALTER SESSION SET/UNSET statements
41677    /// Python: parser.py:7879-7889
41678    pub fn parse_alter_session(&mut self) -> Result<Option<Expression>> {
41679        // ALTER SESSION SET var = value, ...
41680        if self.match_token(TokenType::Set) {
41681            let mut expressions = Vec::new();
41682            loop {
41683                if let Some(item) = self.parse_set_item_assignment()? {
41684                    expressions.push(item);
41685                }
41686                if !self.match_token(TokenType::Comma) {
41687                    break;
41688                }
41689            }
41690            return Ok(Some(Expression::AlterSession(Box::new(AlterSession {
41691                expressions,
41692                unset: None,
41693            }))));
41694        }
41695
41696        // ALTER SESSION UNSET var, ...
41697        if self.match_text_seq(&["UNSET"]) {
41698            let mut expressions = Vec::new();
41699            loop {
41700                if let Some(var) = self.parse_id_var()? {
41701                    // For UNSET, we just use the identifier directly
41702                    expressions.push(var);
41703                }
41704                if !self.match_token(TokenType::Comma) {
41705                    break;
41706                }
41707            }
41708            return Ok(Some(Expression::AlterSession(Box::new(AlterSession {
41709                expressions,
41710                unset: Some(Box::new(Expression::Boolean(BooleanLiteral {
41711                    value: true,
41712                }))),
41713            }))));
41714        }
41715
41716        Ok(None)
41717    }
41718
41719    /// parse_alter_sortkey - Parses ALTER TABLE SORTKEY clause (Redshift)
41720    /// Python: parser.py:7804-7816
41721    pub fn parse_alter_sortkey(&mut self) -> Result<Option<Expression>> {
41722        self.parse_alter_sortkey_impl(None)
41723    }
41724
41725    /// Implementation of parse_alter_sortkey with compound option
41726    pub fn parse_alter_sortkey_impl(
41727        &mut self,
41728        compound: Option<bool>,
41729    ) -> Result<Option<Expression>> {
41730        // For compound sortkey, match SORTKEY keyword
41731        if compound == Some(true) {
41732            self.match_text_seq(&["SORTKEY"]);
41733        }
41734
41735        // Check for (column_list) syntax
41736        if self.check(TokenType::LParen) {
41737            let wrapped = self.parse_wrapped_id_vars()?;
41738            // Extract expressions from Tuple
41739            let expressions = if let Some(Expression::Tuple(t)) = wrapped {
41740                t.expressions
41741            } else {
41742                Vec::new()
41743            };
41744            return Ok(Some(Expression::AlterSortKey(Box::new(AlterSortKey {
41745                this: None,
41746                expressions,
41747                compound: compound
41748                    .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
41749            }))));
41750        }
41751
41752        // Check for AUTO or NONE
41753        if self.match_texts(&["AUTO", "NONE"]) {
41754            let style = self.previous().text.to_ascii_uppercase();
41755            return Ok(Some(Expression::AlterSortKey(Box::new(AlterSortKey {
41756                this: Some(Box::new(Expression::Identifier(Identifier::new(style)))),
41757                expressions: Vec::new(),
41758                compound: compound
41759                    .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
41760            }))));
41761        }
41762
41763        Ok(None)
41764    }
41765
41766    /// parse_alter_table_add - Parses ALTER TABLE ADD clause
41767    /// Python: parser.py:7715-7751
41768    pub fn parse_alter_table_add(&mut self) -> Result<Option<Expression>> {
41769        // Check for ADD keyword (optional in some contexts)
41770        self.match_text_seq(&["ADD"]);
41771
41772        // Check for INDEX/KEY with optional FULLTEXT/SPATIAL prefix (MySQL)
41773        // Syntax: ADD [FULLTEXT|SPATIAL] {INDEX|KEY} [name] (columns) [USING {BTREE|HASH}]
41774        let kind = if self.match_identifier("FULLTEXT") {
41775            Some("FULLTEXT".to_string())
41776        } else if self.match_identifier("SPATIAL") {
41777            Some("SPATIAL".to_string())
41778        } else {
41779            None
41780        };
41781
41782        if self.check(TokenType::Index) || self.check(TokenType::Key) || kind.is_some() {
41783            // Consume INDEX or KEY keyword, track which was used
41784            let use_key_keyword = if self.match_token(TokenType::Key) {
41785                true
41786            } else {
41787                self.match_token(TokenType::Index);
41788                false
41789            };
41790
41791            // Optional index name (before the columns)
41792            let name = if !self.check(TokenType::LParen) && !self.check(TokenType::Using) {
41793                Some(self.expect_identifier_with_quoted()?)
41794            } else {
41795                None
41796            };
41797
41798            // Parse columns (with optional prefix length and DESC)
41799            self.expect(TokenType::LParen)?;
41800            let columns = self.parse_index_identifier_list()?;
41801            self.expect(TokenType::RParen)?;
41802
41803            // Parse optional USING BTREE|HASH
41804            let modifiers = self.parse_constraint_modifiers();
41805
41806            return Ok(Some(Expression::AlterTable(Box::new(AlterTable {
41807                name: TableRef::new(""),
41808                actions: vec![AlterTableAction::AddConstraint(TableConstraint::Index {
41809                    name,
41810                    columns,
41811                    kind,
41812                    modifiers,
41813                    use_key_keyword,
41814                    expression: None,
41815                    index_type: None,
41816                    granularity: None,
41817                })],
41818                if_exists: false,
41819                algorithm: None,
41820                lock: None,
41821                with_check: None,
41822                partition: None,
41823                on_cluster: None,
41824                table_modifier: None,
41825            }))));
41826        }
41827
41828        // Check for constraint keywords (PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, CONSTRAINT)
41829        if self.check(TokenType::PrimaryKey)
41830            || self.check(TokenType::ForeignKey)
41831            || self.check(TokenType::Unique)
41832            || self.check(TokenType::Check)
41833            || self.check(TokenType::Constraint)
41834        {
41835            // Parse a single constraint and return it wrapped in Constraint
41836            if let Some(constraint) = self.parse_constraint()? {
41837                return Ok(Some(Expression::Constraint(Box::new(Constraint {
41838                    this: Box::new(constraint),
41839                    expressions: Vec::new(),
41840                }))));
41841            }
41842        }
41843
41844        // Check for COLUMNS keyword (batch column addition)
41845        if self.match_text_seq(&["COLUMNS"]) {
41846            // Parse schema or column definitions
41847            if let Some(schema) = self.parse_schema()? {
41848                return Ok(Some(schema));
41849            }
41850        }
41851
41852        // Check for IF NOT EXISTS PARTITION (must check before parse_add_column)
41853        let exists = self.match_keywords(&[TokenType::If, TokenType::Not, TokenType::Exists]);
41854        if self.match_token(TokenType::Partition) {
41855            // Parse PARTITION(key = value, ...)
41856            self.expect(TokenType::LParen)?;
41857            let mut partition_exprs = Vec::new();
41858            loop {
41859                if let Some(expr) = self.parse_conjunction()? {
41860                    partition_exprs.push(expr);
41861                }
41862                if !self.match_token(TokenType::Comma) {
41863                    break;
41864                }
41865            }
41866            self.expect(TokenType::RParen)?;
41867
41868            let partition = Expression::Partition(Box::new(crate::expressions::Partition {
41869                expressions: partition_exprs,
41870                subpartition: false,
41871            }));
41872
41873            let location = if self.match_text_seq(&["LOCATION"]) {
41874                self.parse_property()?
41875            } else {
41876                None
41877            };
41878            return Ok(Some(Expression::AddPartition(Box::new(AddPartition {
41879                this: Box::new(partition),
41880                exists,
41881                location: location.map(Box::new),
41882            }))));
41883        }
41884
41885        // Try to parse column definition (after checking for PARTITION)
41886        if let Some(column) = self.parse_add_column()? {
41887            return Ok(Some(column));
41888        }
41889
41890        Ok(None)
41891    }
41892
41893    /// parse_alter_table_alter - Parses ALTER TABLE ALTER COLUMN clause
41894    /// Python: parser.py:7753-7795
41895    pub fn parse_alter_table_alter(&mut self) -> Result<Option<Expression>> {
41896        // Match optional COLUMN keyword
41897        self.match_token(TokenType::Column);
41898
41899        // Parse the column name - required for ALTER COLUMN
41900        let column = match self.parse_field()? {
41901            Some(c) => c,
41902            None => return Ok(None),
41903        };
41904
41905        // DROP DEFAULT
41906        if self.match_keywords(&[TokenType::Drop, TokenType::Default]) {
41907            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41908                this: Box::new(column),
41909                dtype: None,
41910                collate: None,
41911                using: None,
41912                default: None,
41913                drop: Some(Box::new(Expression::Boolean(BooleanLiteral {
41914                    value: true,
41915                }))),
41916                allow_null: None,
41917                comment: None,
41918                visible: None,
41919                rename_to: None,
41920            }))));
41921        }
41922
41923        // SET DEFAULT expr
41924        if self.match_keywords(&[TokenType::Set, TokenType::Default]) {
41925            let default_val = self.parse_disjunction()?;
41926            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41927                this: Box::new(column),
41928                dtype: None,
41929                collate: None,
41930                using: None,
41931                default: default_val.map(Box::new),
41932                drop: None,
41933                allow_null: None,
41934                comment: None,
41935                visible: None,
41936                rename_to: None,
41937            }))));
41938        }
41939
41940        // COMMENT 'string'
41941        if self.match_token(TokenType::Comment) {
41942            let comment_val = self.parse_string()?;
41943            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41944                this: Box::new(column),
41945                dtype: None,
41946                collate: None,
41947                using: None,
41948                default: None,
41949                drop: None,
41950                allow_null: None,
41951                comment: comment_val.map(Box::new),
41952                visible: None,
41953                rename_to: None,
41954            }))));
41955        }
41956
41957        // DROP NOT NULL
41958        if self.match_text_seq(&["DROP", "NOT", "NULL"]) {
41959            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41960                this: Box::new(column),
41961                dtype: None,
41962                collate: None,
41963                using: None,
41964                default: None,
41965                drop: Some(Box::new(Expression::Boolean(BooleanLiteral {
41966                    value: true,
41967                }))),
41968                allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
41969                    value: true,
41970                }))),
41971                comment: None,
41972                visible: None,
41973                rename_to: None,
41974            }))));
41975        }
41976
41977        // SET NOT NULL
41978        if self.match_text_seq(&["SET", "NOT", "NULL"]) {
41979            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41980                this: Box::new(column),
41981                dtype: None,
41982                collate: None,
41983                using: None,
41984                default: None,
41985                drop: None,
41986                allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
41987                    value: false,
41988                }))),
41989                comment: None,
41990                visible: None,
41991                rename_to: None,
41992            }))));
41993        }
41994
41995        // SET VISIBLE
41996        if self.match_text_seq(&["SET", "VISIBLE"]) {
41997            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
41998                this: Box::new(column),
41999                dtype: None,
42000                collate: None,
42001                using: None,
42002                default: None,
42003                drop: None,
42004                allow_null: None,
42005                comment: None,
42006                visible: Some(Box::new(Expression::Identifier(Identifier::new(
42007                    "VISIBLE".to_string(),
42008                )))),
42009                rename_to: None,
42010            }))));
42011        }
42012
42013        // SET INVISIBLE
42014        if self.match_text_seq(&["SET", "INVISIBLE"]) {
42015            return Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
42016                this: Box::new(column),
42017                dtype: None,
42018                collate: None,
42019                using: None,
42020                default: None,
42021                drop: None,
42022                allow_null: None,
42023                comment: None,
42024                visible: Some(Box::new(Expression::Identifier(Identifier::new(
42025                    "INVISIBLE".to_string(),
42026                )))),
42027                rename_to: None,
42028            }))));
42029        }
42030
42031        // [SET DATA] TYPE type [COLLATE collation] [USING expr]
42032        self.match_text_seq(&["SET", "DATA"]);
42033        self.match_text_seq(&["TYPE"]);
42034
42035        let dtype = self.parse_types()?;
42036        let collate = if self.match_token(TokenType::Collate) {
42037            self.parse_term()?
42038        } else {
42039            None
42040        };
42041        let using = if self.match_token(TokenType::Using) {
42042            self.parse_disjunction()?
42043        } else {
42044            None
42045        };
42046
42047        Ok(Some(Expression::AlterColumn(Box::new(AlterColumn {
42048            this: Box::new(column),
42049            dtype: dtype.map(Box::new),
42050            collate: collate.map(Box::new),
42051            using: using.map(Box::new),
42052            default: None,
42053            drop: None,
42054            allow_null: None,
42055            comment: None,
42056            visible: None,
42057            rename_to: None,
42058        }))))
42059    }
42060
42061    /// Parse ALTER TABLE DROP action
42062    /// Note: Main ALTER TABLE DROP logic is implemented inline in parse_alter_table
42063    /// This method provides a separate entry point for the same functionality
42064    pub fn parse_alter_table_drop(&mut self) -> Result<Option<Expression>> {
42065        // Check for IF EXISTS before PARTITION
42066        let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
42067
42068        // Check if this is DROP PARTITION
42069        if self.check(TokenType::Partition) {
42070            return self.parse_drop_partition_with_exists(exists);
42071        }
42072
42073        // Check for DROP FOREIGN KEY (Oracle/MySQL)
42074        if self.match_keywords(&[TokenType::ForeignKey, TokenType::Key]) {
42075            let name = self.expect_identifier_with_quoted()?;
42076            return Ok(Some(Expression::AlterTable(Box::new(AlterTable {
42077                name: TableRef::new(""),
42078                actions: vec![AlterTableAction::DropForeignKey { name }],
42079                if_exists: false,
42080                algorithm: None,
42081                lock: None,
42082                with_check: None,
42083                partition: None,
42084                on_cluster: None,
42085                table_modifier: None,
42086            }))));
42087        }
42088
42089        // Check for DROP COLUMNS (col1, col2, ...) syntax (Spark/Databricks)
42090        if self.check_identifier("COLUMNS") && self.check_next(TokenType::LParen) {
42091            self.skip(); // consume COLUMNS
42092            self.expect(TokenType::LParen)?;
42093            let mut columns = Vec::new();
42094            loop {
42095                if let Some(col) = self.parse_identifier()? {
42096                    columns.push(col);
42097                }
42098                if !self.match_token(TokenType::Comma) {
42099                    break;
42100                }
42101            }
42102            self.expect(TokenType::RParen)?;
42103            if columns.is_empty() {
42104                return Ok(None);
42105            } else if columns.len() == 1 {
42106                return Ok(Some(columns.remove(0)));
42107            } else {
42108                return Ok(Some(Expression::Tuple(Box::new(Tuple {
42109                    expressions: columns,
42110                }))));
42111            }
42112        }
42113
42114        // Otherwise, parse as DROP COLUMN(s)
42115        let mut columns = Vec::new();
42116
42117        // Parse first column
42118        if let Some(col) = self.parse_drop_column()? {
42119            columns.push(col);
42120        }
42121
42122        // Parse additional columns (comma-separated)
42123        while self.match_token(TokenType::Comma) {
42124            // Match optional DROP keyword before next column
42125            self.match_token(TokenType::Drop);
42126            if let Some(col) = self.parse_drop_column()? {
42127                columns.push(col);
42128            }
42129        }
42130
42131        if columns.is_empty() {
42132            Ok(None)
42133        } else if columns.len() == 1 {
42134            Ok(Some(columns.remove(0)))
42135        } else {
42136            // Multiple columns - wrap in a Tuple
42137            Ok(Some(Expression::Tuple(Box::new(Tuple {
42138                expressions: columns,
42139            }))))
42140        }
42141    }
42142
42143    /// parse_alter_table_rename - Parses ALTER TABLE RENAME clause
42144    /// Python: parser.py:7828-7841
42145    pub fn parse_alter_table_rename(&mut self) -> Result<Option<Expression>> {
42146        // RENAME COLUMN old_name TO new_name
42147        if self.match_token(TokenType::Column) {
42148            let exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
42149            let old_column = match self.parse_column()? {
42150                Some(c) => c,
42151                None => return Ok(None),
42152            };
42153
42154            if !self.match_text_seq(&["TO"]) {
42155                return Ok(None);
42156            }
42157
42158            let new_column = self.parse_column()?;
42159
42160            return Ok(Some(Expression::RenameColumn(Box::new(RenameColumn {
42161                this: Box::new(old_column),
42162                to: new_column.map(Box::new),
42163                exists,
42164            }))));
42165        }
42166
42167        // RENAME TO new_table_name
42168        if self.match_text_seq(&["TO"]) {
42169            // Return the table expression directly - the caller will handle it as a rename target
42170            let new_table = self.parse_table()?;
42171            return Ok(new_table);
42172        }
42173
42174        // SQLite allows: RENAME old_name TO new_name (without COLUMN keyword)
42175        // Try to parse as column rename if followed by identifier and TO
42176        if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
42177            let old_column = match self.parse_column()? {
42178                Some(c) => c,
42179                None => return Ok(None),
42180            };
42181
42182            if self.match_text_seq(&["TO"]) {
42183                let new_column = self.parse_column()?;
42184                return Ok(Some(Expression::RenameColumn(Box::new(RenameColumn {
42185                    this: Box::new(old_column),
42186                    to: new_column.map(Box::new),
42187                    exists: false,
42188                }))));
42189            } else {
42190                // Not TO after identifier - put it back and return error
42191                return Err(self.parse_error("Expected COLUMN or TO after RENAME"));
42192            }
42193        }
42194
42195        Ok(None)
42196    }
42197
42198    /// parse_alter_table_set - Parses ALTER TABLE SET clause
42199    /// Python: parser.py:7843-7877
42200    pub fn parse_alter_table_set(&mut self) -> Result<Option<Expression>> {
42201        let mut alter_set = AlterSet {
42202            expressions: Vec::new(),
42203            option: None,
42204            tablespace: None,
42205            access_method: None,
42206            file_format: None,
42207            copy_options: None,
42208            tag: None,
42209            location: None,
42210            serde: None,
42211        };
42212
42213        // SET AUTHORIZATION [ROLE] user
42214        if self.match_token(TokenType::Authorization) {
42215            let mut auth_text = "AUTHORIZATION ".to_string();
42216            if self.match_texts(&["ROLE"]) {
42217                auth_text.push_str("ROLE ");
42218            }
42219            let user = self.expect_identifier()?;
42220            auth_text.push_str(&user);
42221            alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(auth_text))));
42222            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42223        }
42224
42225        // SET PROPERTIES prop = value, ...
42226        if self.match_text_seq(&["PROPERTIES"]) {
42227            let mut assignments = Vec::new();
42228            loop {
42229                // Parse property name (could be identifier or string literal)
42230                let key = if self.check(TokenType::String) {
42231                    self.parse_string()?.unwrap_or(Expression::Null(Null))
42232                } else {
42233                    let name = self.expect_identifier()?;
42234                    Expression::Identifier(Identifier::new(name))
42235                };
42236                self.expect(TokenType::Eq)?;
42237                // Parse value (could be DEFAULT or an expression)
42238                let value = if self.match_token(TokenType::Default) {
42239                    Expression::Identifier(Identifier::new("DEFAULT".to_string()))
42240                } else {
42241                    self.parse_expression()?
42242                };
42243                assignments.push(Expression::Eq(Box::new(BinaryOp {
42244                    left: key,
42245                    right: value,
42246                    left_comments: Vec::new(),
42247                    operator_comments: Vec::new(),
42248                    trailing_comments: Vec::new(),
42249                    inferred_type: None,
42250                })));
42251                if !self.match_token(TokenType::Comma) {
42252                    break;
42253                }
42254            }
42255            alter_set.expressions = assignments;
42256            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42257        }
42258
42259        // SET (properties) or SET TABLE PROPERTIES (properties)
42260        if self.check(TokenType::LParen) || self.match_text_seq(&["TABLE", "PROPERTIES"]) {
42261            let assignments = self.parse_wrapped_csv_assignments()?;
42262            alter_set.expressions = assignments;
42263            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42264        }
42265
42266        // SET FILESTREAM_ON = value
42267        if self.match_text_seq(&["FILESTREAM_ON"]) {
42268            if let Some(assignment) = self.parse_assignment()? {
42269                alter_set.expressions = vec![assignment];
42270            }
42271            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42272        }
42273
42274        // SET LOGGED or SET UNLOGGED
42275        if self.match_texts(&["LOGGED", "UNLOGGED"]) {
42276            let option = self.previous().text.to_ascii_uppercase();
42277            alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(option))));
42278            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42279        }
42280
42281        // SET WITHOUT CLUSTER or SET WITHOUT OIDS
42282        if self.match_text_seq(&["WITHOUT"]) {
42283            if self.match_texts(&["CLUSTER", "OIDS"]) {
42284                let option = format!("WITHOUT {}", self.previous().text.to_ascii_uppercase());
42285                alter_set.option = Some(Box::new(Expression::Identifier(Identifier::new(option))));
42286                return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42287            }
42288        }
42289
42290        // SET LOCATION path
42291        if self.match_text_seq(&["LOCATION"]) {
42292            let loc = self.parse_field()?;
42293            alter_set.location = loc.map(Box::new);
42294            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42295        }
42296
42297        // SET ACCESS METHOD method
42298        if self.match_text_seq(&["ACCESS", "METHOD"]) {
42299            let method = self.parse_field()?;
42300            alter_set.access_method = method.map(Box::new);
42301            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42302        }
42303
42304        // SET TABLESPACE name
42305        if self.match_text_seq(&["TABLESPACE"]) {
42306            let tablespace = self.parse_field()?;
42307            alter_set.tablespace = tablespace.map(Box::new);
42308            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42309        }
42310
42311        // SET FILE FORMAT format or SET FILEFORMAT format
42312        if self.match_text_seq(&["FILE", "FORMAT"]) || self.match_text_seq(&["FILEFORMAT"]) {
42313            let format = self.parse_field()?;
42314            alter_set.file_format = format.map(Box::new);
42315            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42316        }
42317
42318        // SET STAGE_FILE_FORMAT = (options)
42319        if self.match_text_seq(&["STAGE_FILE_FORMAT"]) {
42320            let options = self.parse_wrapped_options()?;
42321            alter_set.file_format = options.map(Box::new);
42322            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42323        }
42324
42325        // SET STAGE_COPY_OPTIONS = (options)
42326        if self.match_text_seq(&["STAGE_COPY_OPTIONS"]) {
42327            let options = self.parse_wrapped_options()?;
42328            alter_set.copy_options = options.map(Box::new);
42329            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42330        }
42331
42332        // SET TAG or SET TAGS
42333        if self.match_text_seq(&["TAG"]) || self.match_text_seq(&["TAGS"]) {
42334            let mut tags = Vec::new();
42335            loop {
42336                if let Some(assignment) = self.parse_assignment()? {
42337                    tags.push(assignment);
42338                }
42339                if !self.match_token(TokenType::Comma) {
42340                    break;
42341                }
42342            }
42343            if !tags.is_empty() {
42344                alter_set.tag = Some(Box::new(Expression::Tuple(Box::new(Tuple {
42345                    expressions: tags,
42346                }))));
42347            }
42348            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42349        }
42350
42351        // SET SERDE 'class' [WITH SERDEPROPERTIES (...)]
42352        if self.match_text_seq(&["SERDE"]) {
42353            let serde = self.parse_field()?;
42354            alter_set.serde = serde.map(Box::new);
42355
42356            // Parse optional properties
42357            let properties = self.parse_wrapped()?;
42358            if let Some(props) = properties {
42359                alter_set.expressions = vec![props];
42360            }
42361            return Ok(Some(Expression::AlterSet(Box::new(alter_set))));
42362        }
42363
42364        Ok(None)
42365    }
42366
42367    /// Helper to parse wrapped CSV of assignments
42368    fn parse_wrapped_csv_assignments(&mut self) -> Result<Vec<Expression>> {
42369        if !self.match_token(TokenType::LParen) {
42370            return Ok(Vec::new());
42371        }
42372        let mut assignments = Vec::new();
42373        loop {
42374            if let Some(assignment) = self.parse_assignment()? {
42375                assignments.push(assignment);
42376            }
42377            if !self.match_token(TokenType::Comma) {
42378                break;
42379            }
42380        }
42381        self.expect(TokenType::RParen)?;
42382        Ok(assignments)
42383    }
42384
42385    /// parse_analyze - Implemented from Python _parse_analyze
42386    /// Calls: parse_table_parts, parse_number, parse_table
42387    #[allow(unused_variables, unused_mut)]
42388    /// parse_analyze - Parses ANALYZE statement
42389    /// Python: parser.py:7937-7999
42390    pub fn parse_analyze(&mut self) -> Result<Option<Expression>> {
42391        // If no more tokens, return empty Analyze
42392        if self.is_at_end() {
42393            return Ok(Some(Expression::Analyze(Box::new(Analyze {
42394                kind: None,
42395                this: None,
42396                options: Vec::new(),
42397                mode: None,
42398                partition: None,
42399                expression: None,
42400                properties: Vec::new(),
42401                columns: Vec::new(),
42402            }))));
42403        }
42404
42405        // Parse options (VERBOSE, SKIP_LOCKED, etc.)
42406        // StarRocks uses FULL and SAMPLE as options
42407        let mut options = Vec::new();
42408        let analyze_styles = [
42409            "VERBOSE",
42410            "SKIP_LOCKED",
42411            "BUFFER_USAGE_LIMIT",
42412            "FULL",
42413            "SAMPLE",
42414        ];
42415        while self.match_texts(&analyze_styles) {
42416            let style = self.previous().text.to_ascii_uppercase();
42417            if style == "BUFFER_USAGE_LIMIT" {
42418                // Parse number after BUFFER_USAGE_LIMIT
42419                if let Some(num) = self.parse_number()? {
42420                    options.push(Expression::Identifier(Identifier::new(format!(
42421                        "BUFFER_USAGE_LIMIT {}",
42422                        if let Expression::Literal(lit) = &num {
42423                            if let Literal::Number(n) = lit.as_ref() {
42424                                n.clone()
42425                            } else {
42426                                String::new()
42427                            }
42428                        } else {
42429                            String::new()
42430                        }
42431                    ))));
42432                }
42433            } else {
42434                options.push(Expression::Identifier(Identifier::new(style)));
42435            }
42436        }
42437
42438        let mut this: Option<Expression> = None;
42439        let mut kind: Option<String> = None;
42440        let mut inner_expression: Option<Expression> = None;
42441
42442        // Parse TABLE or INDEX
42443        if self.match_token(TokenType::Table) {
42444            kind = Some("TABLE".to_string());
42445            this = self.parse_table_parts()?;
42446        } else if self.match_token(TokenType::Index) {
42447            kind = Some("INDEX".to_string());
42448            this = self.parse_table_parts()?;
42449        } else if self.match_text_seq(&["TABLES"]) {
42450            kind = Some("TABLES".to_string());
42451            if self.match_token(TokenType::From) || self.match_token(TokenType::In) {
42452                let dir = self.previous().text.to_ascii_uppercase();
42453                kind = Some(format!("TABLES {}", dir));
42454                // Parse database name as identifier
42455                let db_name = self.expect_identifier()?;
42456                this = Some(Expression::Identifier(Identifier::new(db_name)));
42457            }
42458        } else if self.match_text_seq(&["DATABASE"]) {
42459            kind = Some("DATABASE".to_string());
42460            this = self.parse_table_parts()?;
42461        } else if self.match_text_seq(&["CLUSTER"]) {
42462            kind = Some("CLUSTER".to_string());
42463            this = self.parse_table_parts()?;
42464        } else if self.match_texts(&["LOCAL", "NO_WRITE_TO_BINLOG"]) {
42465            // MySQL: ANALYZE LOCAL TABLE tbl / ANALYZE NO_WRITE_TO_BINLOG TABLE tbl
42466            let opt_text = self.previous().text.to_ascii_uppercase();
42467            options.push(Expression::Identifier(Identifier::new(opt_text)));
42468            if self.match_token(TokenType::Table) {
42469                kind = Some("TABLE".to_string());
42470            }
42471            this = self.parse_table_parts()?;
42472        } else if self.match_text_seq(&["COMPUTE"]) {
42473            // Check ANALYZE_EXPRESSION_PARSERS keywords before fallback to parse_table_parts
42474            // Python: elif self._match_texts(self.ANALYZE_EXPRESSION_PARSERS)
42475            inner_expression = self.parse_analyze_statistics()?;
42476        } else if self.match_text_seq(&["DELETE"]) {
42477            inner_expression = self.parse_analyze_delete()?;
42478        } else if self.match_text_seq(&["VALIDATE"]) {
42479            inner_expression = self.parse_analyze_validate()?;
42480        } else if self.match_text_seq(&["LIST"]) {
42481            inner_expression = self.parse_analyze_list()?;
42482        } else if self.match_text_seq(&["DROP"]) {
42483            inner_expression = self.parse_analyze_histogram()?;
42484        } else if self.match_text_seq(&["UPDATE"]) {
42485            inner_expression = self.parse_analyze_histogram()?;
42486        } else if self.match_texts(&["ALL", "PREDICATE"]) {
42487            inner_expression = self.parse_analyze_columns()?;
42488        } else {
42489            // Try to parse table directly (empty kind - https://prestodb.io/docs/current/sql/analyze.html)
42490            this = self.parse_table_parts()?;
42491        }
42492
42493        // Parse optional column list: ANALYZE tbl(col1, col2) (PostgreSQL)
42494        let columns = if this.is_some() && self.match_token(TokenType::LParen) {
42495            let mut cols = Vec::new();
42496            loop {
42497                cols.push(self.expect_identifier_or_keyword()?);
42498                if !self.match_token(TokenType::Comma) {
42499                    break;
42500                }
42501            }
42502            self.expect(TokenType::RParen)?;
42503            cols
42504        } else {
42505            Vec::new()
42506        };
42507
42508        // Parse optional PARTITION
42509        let partition = self.parse_partition()?;
42510
42511        // Parse optional WITH SYNC/ASYNC MODE or WITH (prop=val, ...) for Presto
42512        let mut mode = None;
42513        let mut properties = Vec::new();
42514
42515        if self.match_text_seq(&["WITH", "SYNC", "MODE"]) {
42516            mode = Some(Box::new(Expression::Identifier(Identifier::new(
42517                "WITH SYNC MODE".to_string(),
42518            ))));
42519        } else if self.match_text_seq(&["WITH", "ASYNC", "MODE"]) {
42520            mode = Some(Box::new(Expression::Identifier(Identifier::new(
42521                "WITH ASYNC MODE".to_string(),
42522            ))));
42523        } else if self.match_text_seq(&["WITH"]) {
42524            // Presto syntax: ANALYZE tbl WITH (prop1=val1, prop2=val2)
42525            if self.match_token(TokenType::LParen) {
42526                loop {
42527                    // Parse key=value pairs
42528                    let key = self.parse_id_var()?;
42529                    if key.is_none() {
42530                        break;
42531                    }
42532
42533                    // Expect = sign
42534                    if self.match_token(TokenType::Eq) {
42535                        // Parse the value
42536                        let value = self.parse_primary()?;
42537                        if let Some(k) = key {
42538                            properties.push(Expression::Property(Box::new(Property {
42539                                this: Box::new(k),
42540                                value: Some(Box::new(value)),
42541                            })));
42542                        }
42543                    } else if let Some(k) = key {
42544                        // Key without value
42545                        properties.push(Expression::Property(Box::new(Property {
42546                            this: Box::new(k),
42547                            value: None,
42548                        })));
42549                    }
42550
42551                    if !self.match_token(TokenType::Comma) {
42552                        break;
42553                    }
42554                }
42555                self.expect(TokenType::RParen)?;
42556            }
42557        }
42558
42559        // Parse optional inner expressions (COMPUTE, DELETE, etc.)
42560        // Only if inner_expression wasn't already set (for cases like ANALYZE TABLE tbl VALIDATE...)
42561        if inner_expression.is_none() {
42562            if self.match_text_seq(&["COMPUTE"]) {
42563                inner_expression = self.parse_analyze_statistics()?;
42564            } else if self.match_text_seq(&["DELETE"]) {
42565                inner_expression = self.parse_analyze_delete()?;
42566            } else if self.match_text_seq(&["VALIDATE"]) {
42567                inner_expression = self.parse_analyze_validate()?;
42568            } else if self.match_text_seq(&["LIST"]) {
42569                inner_expression = self.parse_analyze_list()?;
42570            } else if self.match_text_seq(&["DROP"]) {
42571                inner_expression = self.parse_analyze_histogram()?;
42572            } else if self.match_text_seq(&["UPDATE"]) {
42573                inner_expression = self.parse_analyze_histogram()?;
42574            } else if self.match_texts(&["ALL", "PREDICATE"]) {
42575                // Redshift: ANALYZE TBL ALL COLUMNS / ANALYZE TBL PREDICATE COLUMNS
42576                inner_expression = self.parse_analyze_columns()?;
42577            }
42578        }
42579
42580        // Parse optional properties (if not already parsed from WITH clause)
42581        // StarRocks syntax: ANALYZE TABLE TBL PROPERTIES ('prop1'=val1, 'prop2'=val2)
42582        if properties.is_empty() && self.match_text_seq(&["PROPERTIES"]) {
42583            if self.match_token(TokenType::LParen) {
42584                loop {
42585                    // Parse key (can be a string literal or identifier)
42586                    let key = if self.check(TokenType::String) {
42587                        self.skip();
42588                        let key_str = self.previous().text.clone();
42589                        Expression::Literal(Box::new(Literal::String(key_str)))
42590                    } else {
42591                        self.parse_id_var()?
42592                            .unwrap_or(Expression::Identifier(Identifier::new(String::new())))
42593                    };
42594
42595                    // Expect = sign
42596                    if self.match_token(TokenType::Eq) {
42597                        // Parse the value
42598                        let value = self.parse_primary()?;
42599                        properties.push(Expression::Property(Box::new(Property {
42600                            this: Box::new(key),
42601                            value: Some(Box::new(value)),
42602                        })));
42603                    } else {
42604                        // Key without value
42605                        properties.push(Expression::Property(Box::new(Property {
42606                            this: Box::new(key),
42607                            value: None,
42608                        })));
42609                    }
42610
42611                    if !self.match_token(TokenType::Comma) {
42612                        break;
42613                    }
42614                }
42615                self.expect(TokenType::RParen)?;
42616            }
42617        }
42618
42619        Ok(Some(Expression::Analyze(Box::new(Analyze {
42620            kind,
42621            this: this.map(Box::new),
42622            options,
42623            mode,
42624            partition: partition.map(Box::new),
42625            expression: inner_expression.map(Box::new),
42626            properties,
42627            columns,
42628        }))))
42629    }
42630
42631    /// parse_analyze_columns - Parses ANALYZE ... COLUMNS
42632    /// Python: parser.py:8055-8059
42633    /// Note: AnalyzeColumns not in expressions.rs, using Identifier instead
42634    pub fn parse_analyze_columns(&mut self) -> Result<Option<Expression>> {
42635        let prev_text = self.previous().text.to_ascii_uppercase();
42636        if self.match_text_seq(&["COLUMNS"]) {
42637            return Ok(Some(Expression::Identifier(Identifier::new(format!(
42638                "{} COLUMNS",
42639                prev_text
42640            )))));
42641        }
42642        Ok(None)
42643    }
42644
42645    /// parse_analyze_delete - Parses ANALYZE DELETE STATISTICS
42646    /// Python: parser.py:8061-8065
42647    pub fn parse_analyze_delete(&mut self) -> Result<Option<Expression>> {
42648        let kind = if self.match_text_seq(&["SYSTEM"]) {
42649            Some("SYSTEM".to_string())
42650        } else {
42651            None
42652        };
42653
42654        if self.match_text_seq(&["STATISTICS"]) {
42655            return Ok(Some(Expression::AnalyzeDelete(Box::new(AnalyzeDelete {
42656                kind,
42657            }))));
42658        }
42659
42660        Ok(None)
42661    }
42662
42663    /// parse_analyze_histogram - Parses ANALYZE ... HISTOGRAM ON
42664    /// Python: parser.py:8073-8108
42665    pub fn parse_analyze_histogram(&mut self) -> Result<Option<Expression>> {
42666        let action = self.previous().text.to_ascii_uppercase(); // DROP or UPDATE
42667        let mut expressions = Vec::new();
42668        let mut update_options: Option<Box<Expression>> = None;
42669        let mut expression: Option<Box<Expression>> = None;
42670
42671        if !self.match_text_seq(&["HISTOGRAM", "ON"]) {
42672            return Ok(None);
42673        }
42674
42675        // Parse column references
42676        loop {
42677            if let Some(col) = self.parse_column_reference()? {
42678                expressions.push(col);
42679            } else {
42680                break;
42681            }
42682            if !self.match_token(TokenType::Comma) {
42683                break;
42684            }
42685        }
42686
42687        // Parse USING DATA 'json_data' (MySQL) - must check before WITH
42688        if self.match_text_seq(&["USING", "DATA"]) {
42689            if self.check(TokenType::String) {
42690                let tok = self.advance();
42691                expression = Some(Box::new(Expression::Identifier(Identifier::new(format!(
42692                    "USING DATA '{}'",
42693                    tok.text
42694                )))));
42695            } else {
42696                expression = Some(Box::new(Expression::Identifier(Identifier::new(
42697                    "USING DATA".to_string(),
42698                ))));
42699            }
42700        }
42701
42702        // Parse WITH options - can have two WITH clauses:
42703        // 1. WITH SYNC/ASYNC MODE (optional)
42704        // 2. WITH n BUCKETS (optional)
42705        // StarRocks syntax: WITH SYNC MODE WITH 5 BUCKETS
42706        let mut mode_str: Option<String> = None;
42707        let mut buckets_str: Option<String> = None;
42708
42709        if self.match_token(TokenType::With) {
42710            if self.match_texts(&["SYNC", "ASYNC"]) {
42711                let mode = self.previous().text.to_ascii_uppercase();
42712                if self.match_text_seq(&["MODE"]) {
42713                    mode_str = Some(format!("WITH {} MODE", mode));
42714                }
42715                // Check for second WITH clause for buckets
42716                if self.match_token(TokenType::With) {
42717                    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            } else if let Some(num) = self.parse_number()? {
42733                if self.match_text_seq(&["BUCKETS"]) {
42734                    let num_str = if let Expression::Literal(lit) = &num {
42735                        if let Literal::Number(n) = lit.as_ref() {
42736                            n.clone()
42737                        } else {
42738                            String::new()
42739                        }
42740                    } else {
42741                        String::new()
42742                    };
42743                    buckets_str = Some(format!("WITH {} BUCKETS", num_str));
42744                }
42745            }
42746        }
42747
42748        // Combine mode and buckets into expression
42749        match (mode_str, buckets_str) {
42750            (Some(m), Some(b)) => {
42751                expression = Some(Box::new(Expression::Identifier(Identifier::new(format!(
42752                    "{} {}",
42753                    m, b
42754                )))));
42755            }
42756            (Some(m), None) => {
42757                expression = Some(Box::new(Expression::Identifier(Identifier::new(m))));
42758            }
42759            (None, Some(b)) => {
42760                expression = Some(Box::new(Expression::Identifier(Identifier::new(b))));
42761            }
42762            (None, None) => {}
42763        }
42764
42765        // Parse AUTO UPDATE or MANUAL UPDATE (MySQL 8.0.27+)
42766        if self.match_texts(&["MANUAL", "AUTO"]) {
42767            let mode = self.previous().text.to_ascii_uppercase();
42768            if self.check(TokenType::Update) {
42769                update_options = Some(Box::new(Expression::Identifier(Identifier::new(mode))));
42770                self.skip(); // consume UPDATE
42771            }
42772        }
42773
42774        Ok(Some(Expression::AnalyzeHistogram(Box::new(
42775            AnalyzeHistogram {
42776                this: Box::new(Expression::Identifier(Identifier::new(action))),
42777                expressions,
42778                expression,
42779                update_options,
42780            },
42781        ))))
42782    }
42783
42784    /// parse_analyze_list - Parses ANALYZE LIST CHAINED ROWS
42785    /// Python: parser.py:8067-8070
42786    pub fn parse_analyze_list(&mut self) -> Result<Option<Expression>> {
42787        if self.match_text_seq(&["CHAINED", "ROWS"]) {
42788            let expression = self.parse_into()?.map(Box::new);
42789            return Ok(Some(Expression::AnalyzeListChainedRows(Box::new(
42790                AnalyzeListChainedRows { expression },
42791            ))));
42792        }
42793        Ok(None)
42794    }
42795
42796    /// parse_analyze_statistics - Parses ANALYZE ... STATISTICS
42797    /// Python: parser.py:8002-8031
42798    pub fn parse_analyze_statistics(&mut self) -> Result<Option<Expression>> {
42799        let kind = self.previous().text.to_ascii_uppercase();
42800        let option = if self.match_text_seq(&["DELTA"]) {
42801            Some(Box::new(Expression::Identifier(Identifier::new(
42802                "DELTA".to_string(),
42803            ))))
42804        } else {
42805            None
42806        };
42807
42808        // Expect STATISTICS keyword
42809        if !self.match_text_seq(&["STATISTICS"]) {
42810            return Ok(None);
42811        }
42812
42813        let mut this: Option<Box<Expression>> = None;
42814        let mut expressions = Vec::new();
42815
42816        if self.match_text_seq(&["NOSCAN"]) {
42817            this = Some(Box::new(Expression::Identifier(Identifier::new(
42818                "NOSCAN".to_string(),
42819            ))));
42820        } else if self.match_token(TokenType::For) {
42821            if self.match_text_seq(&["ALL", "COLUMNS"]) {
42822                this = Some(Box::new(Expression::Identifier(Identifier::new(
42823                    "FOR ALL COLUMNS".to_string(),
42824                ))));
42825            } else if self.match_text_seq(&["COLUMNS"]) {
42826                this = Some(Box::new(Expression::Identifier(Identifier::new(
42827                    "FOR COLUMNS".to_string(),
42828                ))));
42829                // Parse column list
42830                loop {
42831                    if let Some(col) = self.parse_column_reference()? {
42832                        expressions.push(col);
42833                    } else {
42834                        break;
42835                    }
42836                    if !self.match_token(TokenType::Comma) {
42837                        break;
42838                    }
42839                }
42840            }
42841        } else if self.match_text_seq(&["SAMPLE"]) {
42842            // Parse SAMPLE number [PERCENT]
42843            if let Some(sample) = self.parse_number()? {
42844                let sample_kind = if self.match_token(TokenType::Percent) {
42845                    Some("PERCENT".to_string())
42846                } else {
42847                    None
42848                };
42849                expressions.push(Expression::AnalyzeSample(Box::new(AnalyzeSample {
42850                    kind: sample_kind.unwrap_or_default(),
42851                    sample: Some(Box::new(sample)),
42852                })));
42853            }
42854        }
42855
42856        Ok(Some(Expression::AnalyzeStatistics(Box::new(
42857            AnalyzeStatistics {
42858                kind,
42859                option,
42860                this,
42861                expressions,
42862            },
42863        ))))
42864    }
42865
42866    /// parse_analyze_validate - Parses ANALYZE VALIDATE
42867    /// Python: parser.py:8034-8053
42868    pub fn parse_analyze_validate(&mut self) -> Result<Option<Expression>> {
42869        let mut kind = String::new();
42870        let mut this: Option<Box<Expression>> = None;
42871        let mut expression: Option<Box<Expression>> = None;
42872
42873        if self.match_text_seq(&["REF", "UPDATE"]) {
42874            kind = "REF".to_string();
42875            this = Some(Box::new(Expression::Identifier(Identifier::new(
42876                "UPDATE".to_string(),
42877            ))));
42878            if self.match_text_seq(&["SET", "DANGLING", "TO", "NULL"]) {
42879                this = Some(Box::new(Expression::Identifier(Identifier::new(
42880                    "UPDATE SET DANGLING TO NULL".to_string(),
42881                ))));
42882            }
42883        } else if self.match_text_seq(&["STRUCTURE"]) {
42884            kind = "STRUCTURE".to_string();
42885            if self.match_text_seq(&["CASCADE", "FAST"]) {
42886                this = Some(Box::new(Expression::Identifier(Identifier::new(
42887                    "CASCADE FAST".to_string(),
42888                ))));
42889            } else if self.match_text_seq(&["CASCADE", "COMPLETE"]) {
42890                if self.match_texts(&["ONLINE", "OFFLINE"]) {
42891                    let mode = self.previous().text.to_ascii_uppercase();
42892                    this = Some(Box::new(Expression::Identifier(Identifier::new(format!(
42893                        "CASCADE COMPLETE {}",
42894                        mode
42895                    )))));
42896                    expression = self.parse_into()?.map(Box::new);
42897                }
42898            }
42899        }
42900
42901        if kind.is_empty() {
42902            return Ok(None);
42903        }
42904
42905        Ok(Some(Expression::AnalyzeValidate(Box::new(
42906            AnalyzeValidate {
42907                kind,
42908                this,
42909                expression,
42910            },
42911        ))))
42912    }
42913
42914    /// parse_attach_detach - Parses ATTACH/DETACH statements (DuckDB)
42915    /// Python: DuckDB._parse_attach_detach
42916    pub fn parse_attach_detach(&mut self, is_attach: bool) -> Result<Expression> {
42917        // ATTACH [DATABASE] [IF NOT EXISTS] 'path' [AS alias] [(options)]
42918        // DETACH [DATABASE] [IF EXISTS] name
42919        // DATABASE can be tokenized as TokenType::Database (keyword), not just Var
42920        let _ = self.match_identifier("DATABASE") || self.match_token(TokenType::Database);
42921
42922        let exists = if is_attach {
42923            self.match_text_seq(&["IF", "NOT", "EXISTS"])
42924        } else {
42925            self.match_text_seq(&["IF", "EXISTS"])
42926        };
42927
42928        // Parse the expression (can be a path string, identifier, or expression like 'foo' || '.foo2'
42929        // or NOT EXISTS(subquery) for conditional attach)
42930        let this_expr = self.parse_expression()?;
42931
42932        // Check for AS alias
42933        let this = if self.match_token(TokenType::As) {
42934            let alias = self.expect_identifier_or_keyword_with_quoted()?;
42935            Expression::Alias(Box::new(Alias {
42936                this: this_expr,
42937                alias,
42938                column_aliases: Vec::new(),
42939                pre_alias_comments: Vec::new(),
42940                trailing_comments: Vec::new(),
42941                inferred_type: None,
42942            }))
42943        } else {
42944            this_expr
42945        };
42946
42947        if is_attach {
42948            // Parse optional (options)
42949            let expressions = if self.match_token(TokenType::LParen) {
42950                let mut opts = Vec::new();
42951                loop {
42952                    // Parse option: KEY [VALUE]
42953                    let key_name = self.advance().text.to_ascii_uppercase();
42954                    let key = Expression::Identifier(Identifier::new(key_name));
42955                    let value = if !self.check(TokenType::Comma) && !self.check(TokenType::RParen) {
42956                        // The value can be an identifier, string, boolean, etc.
42957                        let val_token = self.advance();
42958                        let val_expr = if val_token.token_type == TokenType::String {
42959                            Expression::Literal(Box::new(Literal::String(val_token.text.clone())))
42960                        } else if val_token.token_type == TokenType::True {
42961                            Expression::Boolean(BooleanLiteral { value: true })
42962                        } else if val_token.token_type == TokenType::False {
42963                            Expression::Boolean(BooleanLiteral { value: false })
42964                        } else {
42965                            Expression::Identifier(Identifier::new(val_token.text.clone()))
42966                        };
42967                        Some(Box::new(val_expr))
42968                    } else {
42969                        None
42970                    };
42971                    opts.push(Expression::AttachOption(Box::new(AttachOption {
42972                        this: Box::new(key),
42973                        expression: value,
42974                    })));
42975                    if !self.match_token(TokenType::Comma) {
42976                        break;
42977                    }
42978                }
42979                self.expect(TokenType::RParen)?;
42980                opts
42981            } else {
42982                Vec::new()
42983            };
42984
42985            Ok(Expression::Attach(Box::new(Attach {
42986                this: Box::new(this),
42987                exists,
42988                expressions,
42989            })))
42990        } else {
42991            Ok(Expression::Detach(Box::new(Detach {
42992                this: Box::new(this),
42993                exists,
42994            })))
42995        }
42996    }
42997
42998    /// parse_install - Parses INSTALL statement (DuckDB)
42999    /// Python: DuckDB._parse_install
43000    pub fn parse_install(&mut self, force: bool) -> Result<Expression> {
43001        // INSTALL extension [FROM source]
43002        let name = self.expect_identifier_or_keyword()?;
43003        let this = Expression::Identifier(Identifier::new(name));
43004
43005        let from_ = if self.match_token(TokenType::From) {
43006            // FROM can be followed by a string or identifier
43007            Some(Box::new(self.parse_primary()?))
43008        } else {
43009            None
43010        };
43011
43012        Ok(Expression::Install(Box::new(Install {
43013            this: Box::new(this),
43014            from_,
43015            force: if force {
43016                Some(Box::new(Expression::Boolean(BooleanLiteral {
43017                    value: true,
43018                })))
43019            } else {
43020                None
43021            },
43022        })))
43023    }
43024
43025    /// parse_force_statement - Parses FORCE INSTALL/CHECKPOINT (DuckDB)
43026    /// Python: DuckDB._parse_force
43027    pub fn parse_force_statement(&mut self) -> Result<Expression> {
43028        if self.match_identifier("INSTALL") {
43029            return self.parse_install(true);
43030        }
43031        // FORCE CHECKPOINT or other: fallback to command
43032        self.parse_as_command()?
43033            .ok_or_else(|| self.parse_error("Failed to parse FORCE statement"))
43034    }
43035
43036    /// parse_summarize_statement - Parses SUMMARIZE statement (DuckDB)
43037    /// Python: DuckDB parser for SUMMARIZE
43038    pub fn parse_summarize_statement(&mut self) -> Result<Expression> {
43039        // SUMMARIZE [TABLE] expression
43040        let is_table = self.match_token(TokenType::Table);
43041
43042        // Try to parse a SELECT statement, string, or table reference
43043        let this = if self.check(TokenType::Select) || self.check(TokenType::With) {
43044            self.parse_select()?
43045        } else if self.check(TokenType::String) {
43046            self.parse_primary()?
43047        } else {
43048            // Parse as table name
43049            self.parse_table_parts()?
43050                .unwrap_or(Expression::Identifier(Identifier::new(String::new())))
43051        };
43052
43053        Ok(Expression::Summarize(Box::new(Summarize {
43054            this: Box::new(this),
43055            table: if is_table {
43056                Some(Box::new(Expression::Boolean(BooleanLiteral {
43057                    value: true,
43058                })))
43059            } else {
43060                None
43061            },
43062        })))
43063    }
43064
43065    /// parse_deallocate_prepare - Parses DEALLOCATE PREPARE <name>
43066    /// Presto/Trino syntax for deallocating prepared statements
43067    pub fn parse_deallocate_prepare(&mut self) -> Result<Expression> {
43068        self.skip(); // consume DEALLOCATE
43069
43070        // Check for PREPARE keyword
43071        if self.match_identifier("PREPARE") {
43072            // Parse the statement name
43073            let name = if !self.is_at_end() && !self.check(TokenType::Semicolon) {
43074                self.advance().text.clone()
43075            } else {
43076                String::new()
43077            };
43078
43079            // Build the command text
43080            let command_text = if name.is_empty() {
43081                "DEALLOCATE PREPARE".to_string()
43082            } else {
43083                format!("DEALLOCATE PREPARE {}", name)
43084            };
43085
43086            Ok(Expression::Command(Box::new(Command {
43087                this: command_text,
43088            })))
43089        } else {
43090            // Just DEALLOCATE without PREPARE - consume rest as command
43091            let mut parts = vec!["DEALLOCATE".to_string()];
43092            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
43093                let token = self.advance();
43094                parts.push(token.text.clone());
43095            }
43096            Ok(Expression::Command(Box::new(Command {
43097                this: parts.join(" "),
43098            })))
43099        }
43100    }
43101
43102    /// parse_as_command - Creates Command expression
43103    #[allow(unused_variables, unused_mut)]
43104    /// parse_as_command - Parses remaining tokens as a raw command
43105    /// Python: _parse_as_command
43106    /// Used as fallback when specific parsing fails
43107    pub fn parse_as_command(&mut self) -> Result<Option<Expression>> {
43108        // Get the starting token text
43109        let start_text = if self.current > 0 {
43110            self.tokens
43111                .get(self.current - 1)
43112                .map(|t| t.text.clone())
43113                .unwrap_or_default()
43114        } else {
43115            String::new()
43116        };
43117
43118        // Consume all remaining tokens, storing both text and type
43119        let mut tokens_info: Vec<(String, TokenType)> = Vec::new();
43120        while !self.is_at_end() {
43121            let token = self.advance();
43122            tokens_info.push((token.text.clone(), token.token_type.clone()));
43123        }
43124
43125        // Join tokens intelligently, avoiding spaces around punctuation
43126        let mut expression = String::new();
43127        for (i, (text, token_type)) in tokens_info.iter().enumerate() {
43128            if i > 0 {
43129                // Check if we should add a space before this token
43130                let prev_type = &tokens_info[i - 1].1;
43131                let needs_space = !Self::is_punctuation_token(prev_type)
43132                    && !Self::is_punctuation_token(token_type);
43133                if needs_space {
43134                    expression.push(' ');
43135                }
43136            }
43137            expression.push_str(text);
43138        }
43139
43140        Ok(Some(Expression::Command(Box::new(Command {
43141            this: if expression.is_empty() {
43142                start_text
43143            } else {
43144                format!("{} {}", start_text, expression)
43145            },
43146        }))))
43147    }
43148
43149    /// Helper to determine if a token type is punctuation that shouldn't have spaces around it
43150    fn is_punctuation_token(token_type: &TokenType) -> bool {
43151        matches!(
43152            token_type,
43153            TokenType::Dot | TokenType::Colon | TokenType::DColon
43154        )
43155    }
43156
43157    /// Fallback to Command expression from a saved position.
43158    /// Extracts verbatim SQL text from source if available, consuming tokens until semicolon/EOF.
43159    fn fallback_to_command(&mut self, start_pos: usize) -> Result<Expression> {
43160        let start_span = self.tokens[start_pos].span.start;
43161        // Consume until semicolon or end
43162        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
43163            self.skip();
43164        }
43165        let command_text = if let Some(ref source) = self.source {
43166            let end_span = if self.current > 0 {
43167                self.tokens[self.current - 1].span.end
43168            } else {
43169                start_span
43170            };
43171            source[start_span..end_span].trim().to_string()
43172        } else {
43173            // Fallback: join token texts
43174            let mut parts = Vec::new();
43175            for i in start_pos..self.current {
43176                if self.tokens[i].token_type == TokenType::String {
43177                    parts.push(format!("'{}'", self.tokens[i].text.replace('\'', "''")));
43178                } else {
43179                    parts.push(self.tokens[i].text.clone());
43180                }
43181            }
43182            parts.join(" ")
43183        };
43184        Ok(Expression::Command(Box::new(Command {
43185            this: command_text,
43186        })))
43187    }
43188
43189    /// parse_assignment - Parses assignment expressions (variable := value)
43190    /// Python: _parse_assignment
43191    pub fn parse_assignment(&mut self) -> Result<Option<Expression>> {
43192        // First parse a disjunction (left side of potential assignment)
43193        let mut this = self.parse_disjunction()?;
43194
43195        // Handle := assignment operator
43196        while self.match_token(TokenType::ColonEq) {
43197            if let Some(left) = this {
43198                let right = self.parse_assignment()?;
43199                if let Some(right_expr) = right {
43200                    this = Some(Expression::PropertyEQ(Box::new(BinaryOp {
43201                        left,
43202                        right: right_expr,
43203                        left_comments: Vec::new(),
43204                        operator_comments: Vec::new(),
43205                        trailing_comments: Vec::new(),
43206                        inferred_type: None,
43207                    })));
43208                } else {
43209                    this = Some(left);
43210                    break;
43211                }
43212            } else {
43213                break;
43214            }
43215        }
43216
43217        // ClickHouse ternary operator: condition ? true_value : false_value
43218        // Parsed as: If(this=condition, true=true_value, false=false_value)
43219        if matches!(
43220            self.config.dialect,
43221            Some(crate::dialects::DialectType::ClickHouse)
43222        ) {
43223            if let Some(condition) = this {
43224                if self.match_token(TokenType::Parameter) {
43225                    if self.check(TokenType::Colon) {
43226                        return Err(self.parse_error(
43227                            "Expected true expression after ? in ClickHouse ternary",
43228                        ));
43229                    }
43230                    let true_value = self.parse_assignment()?.ok_or_else(|| {
43231                        self.parse_error("Expected true expression after ? in ClickHouse ternary")
43232                    })?;
43233                    let false_value = if self.match_token(TokenType::Colon) {
43234                        self.parse_assignment()?.unwrap_or(Expression::Null(Null))
43235                    } else {
43236                        Expression::Null(Null)
43237                    };
43238                    return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
43239                        original_name: None,
43240                        condition,
43241                        true_value,
43242                        false_value: Some(false_value),
43243                        inferred_type: None,
43244                    }))));
43245                }
43246                this = Some(condition);
43247            }
43248        }
43249
43250        Ok(this)
43251    }
43252
43253    /// parse_auto_increment - Implemented from Python _parse_auto_increment
43254    /// Calls: parse_bitwise
43255    #[allow(unused_variables, unused_mut)]
43256    pub fn parse_auto_increment(&mut self) -> Result<Option<Expression>> {
43257        if self.match_text_seq(&["START"]) {
43258            return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
43259                Box::new(GeneratedAsIdentityColumnConstraint {
43260                    this: None,
43261                    expression: None,
43262                    on_null: None,
43263                    start: None,
43264                    increment: None,
43265                    minvalue: None,
43266                    maxvalue: None,
43267                    cycle: None,
43268                    order: None,
43269                }),
43270            )));
43271        }
43272        if self.match_text_seq(&["INCREMENT"]) {
43273            // Matched: INCREMENT
43274            return Ok(None);
43275        }
43276        if self.match_text_seq(&["ORDER"]) {
43277            // Matched: ORDER
43278            return Ok(None);
43279        }
43280        Ok(None)
43281    }
43282
43283    /// parse_auto_property - Implemented from Python _parse_auto_property
43284    #[allow(unused_variables, unused_mut)]
43285    pub fn parse_auto_property(&mut self) -> Result<Option<Expression>> {
43286        if self.match_text_seq(&["REFRESH"]) {
43287            // Matched: REFRESH
43288            return Ok(None);
43289        }
43290        Ok(None)
43291    }
43292
43293    /// parse_between - Implemented from Python _parse_between
43294    #[allow(unused_variables, unused_mut)]
43295    pub fn parse_between(&mut self) -> Result<Option<Expression>> {
43296        if self.match_text_seq(&["SYMMETRIC"]) {
43297            // Matched: SYMMETRIC
43298            return Ok(None);
43299        }
43300        if self.match_text_seq(&["ASYMMETRIC"]) {
43301            // Matched: ASYMMETRIC
43302            return Ok(None);
43303        }
43304        Ok(None)
43305    }
43306
43307    /// parse_bitwise - Parses bitwise OR/XOR/AND expressions
43308    /// Python: _parse_bitwise
43309    /// Delegates to the existing parse_bitwise_or in the operator precedence chain
43310    pub fn parse_bitwise(&mut self) -> Result<Option<Expression>> {
43311        let start = self.current;
43312        match self.parse_bitwise_or() {
43313            Ok(expr) => Ok(Some(expr)),
43314            Err(_err) if self.current == start => Ok(None),
43315            Err(err) => Err(err),
43316        }
43317    }
43318
43319    /// parse_blockcompression - Implemented from Python _parse_blockcompression
43320    #[allow(unused_variables, unused_mut)]
43321    pub fn parse_blockcompression(&mut self) -> Result<Option<Expression>> {
43322        if self.match_text_seq(&["ALWAYS"]) {
43323            return Ok(Some(Expression::BlockCompressionProperty(Box::new(
43324                BlockCompressionProperty {
43325                    autotemp: None,
43326                    always: None,
43327                    default: None,
43328                    manual: None,
43329                    never: None,
43330                },
43331            ))));
43332        }
43333        if self.match_text_seq(&["MANUAL"]) {
43334            // Matched: MANUAL
43335            return Ok(None);
43336        }
43337        Ok(None)
43338    }
43339
43340    /// parse_boolean - Parse boolean literal (TRUE/FALSE)
43341    /// Python: if self._match(TokenType.TRUE): return exp.Boolean(this=True)
43342    pub fn parse_boolean(&mut self) -> Result<Option<Expression>> {
43343        if self.match_token(TokenType::True) {
43344            return Ok(Some(Expression::Boolean(BooleanLiteral { value: true })));
43345        }
43346        if self.match_token(TokenType::False) {
43347            return Ok(Some(Expression::Boolean(BooleanLiteral { value: false })));
43348        }
43349        Ok(None)
43350    }
43351
43352    /// parse_bracket - Ported from Python _parse_bracket
43353    /// Parses bracket expressions: array[index], array literal [1,2,3], or struct {key: value}
43354    #[allow(unused_variables, unused_mut)]
43355    pub fn parse_bracket(&mut self) -> Result<Option<Expression>> {
43356        self.parse_bracket_with_expr(None)
43357    }
43358
43359    /// parse_bracket_with_expr - Parses bracket with optional left-side expression
43360    fn parse_bracket_with_expr(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
43361        // Check for [ or {
43362        let is_bracket = self.match_token(TokenType::LBracket);
43363        let is_brace = if !is_bracket {
43364            self.match_token(TokenType::LBrace)
43365        } else {
43366            false
43367        };
43368
43369        if !is_bracket && !is_brace {
43370            return Ok(this);
43371        }
43372
43373        // Parse comma-separated expressions inside brackets
43374        let mut expressions: Vec<Expression> = Vec::new();
43375
43376        if is_bracket && !self.check(TokenType::RBracket) {
43377            // Check for slice syntax at the start: [:...] or [:-...]
43378            // This needs to be detected before parse_bracket_key_value which calls parse_primary
43379            // and parse_primary would consume : as a parameter prefix
43380            let first_expr = if self.check(TokenType::Colon) {
43381                // This is slice syntax like [:] or [:-1] or [::step]
43382                // Parse it using slice parser with no 'this'
43383                if let Some(slice) = self.parse_slice()? {
43384                    slice
43385                } else {
43386                    self.parse_expression()?
43387                }
43388            } else if let Ok(Some(expr)) = self.parse_bracket_key_value() {
43389                expr
43390            } else {
43391                // Parse regular expression and check for slice
43392                let expr = self.parse_expression()?;
43393                // Check if followed by colon (slice syntax like [start:end])
43394                if self.check(TokenType::Colon) {
43395                    if let Some(slice) = self.parse_slice_with_this(Some(expr))? {
43396                        slice
43397                    } else {
43398                        return Err(self.parse_error("Failed to parse slice"));
43399                    }
43400                } else {
43401                    expr
43402                }
43403            };
43404
43405            // Check for comprehension syntax: [expr FOR var IN iterator [IF condition]]
43406            if self.match_token(TokenType::For) {
43407                // Parse loop variable - typically a simple identifier like 'x'
43408                let loop_var = self.parse_primary()?;
43409
43410                // Parse optional position (second variable after comma)
43411                let position = if self.match_token(TokenType::Comma) {
43412                    Some(self.parse_primary()?)
43413                } else {
43414                    None
43415                };
43416
43417                // Expect IN keyword
43418                if !self.match_token(TokenType::In) {
43419                    return Err(self.parse_error("Expected IN in comprehension"));
43420                }
43421
43422                // Parse iterator expression
43423                let iterator = self.parse_expression()?;
43424
43425                // Parse optional condition after IF
43426                let condition = if self.match_token(TokenType::If) {
43427                    Some(self.parse_expression()?)
43428                } else {
43429                    None
43430                };
43431
43432                // Expect closing bracket
43433                self.expect(TokenType::RBracket)?;
43434
43435                // Return Comprehension wrapped in an expression
43436                return Ok(Some(Expression::Comprehension(Box::new(Comprehension {
43437                    this: Box::new(first_expr),
43438                    expression: Box::new(loop_var),
43439                    position: position.map(Box::new),
43440                    iterator: Some(Box::new(iterator)),
43441                    condition: condition.map(Box::new),
43442                }))));
43443            }
43444
43445            expressions.push(first_expr);
43446
43447            // Continue parsing remaining expressions
43448            while self.match_token(TokenType::Comma) {
43449                if let Ok(Some(expr)) = self.parse_bracket_key_value() {
43450                    expressions.push(expr);
43451                } else {
43452                    match self.parse_expression() {
43453                        Ok(expr) => expressions.push(expr),
43454                        Err(_) => break,
43455                    }
43456                }
43457            }
43458        } else if is_brace && !self.check(TokenType::RBrace) {
43459            loop {
43460                if let Ok(Some(expr)) = self.parse_bracket_key_value() {
43461                    expressions.push(expr);
43462                } else {
43463                    match self.parse_expression() {
43464                        Ok(expr) => expressions.push(expr),
43465                        Err(_) => break,
43466                    }
43467                }
43468                if !self.match_token(TokenType::Comma) {
43469                    break;
43470                }
43471            }
43472        }
43473
43474        // Expect closing bracket
43475        if is_bracket {
43476            self.expect(TokenType::RBracket)?;
43477        } else if is_brace {
43478            self.expect(TokenType::RBrace)?;
43479        }
43480
43481        // Build the result
43482        if is_brace {
43483            // Struct literal: {key: value, ...}
43484            // Convert expressions to (Option<name>, expr) pairs
43485            let fields: Vec<(Option<String>, Expression)> =
43486                expressions.into_iter().map(|e| (None, e)).collect();
43487            Ok(Some(Expression::Struct(Box::new(Struct { fields }))))
43488        } else if let Some(base_expr) = this {
43489            // Subscript access: base[index]
43490            if expressions.len() == 1 {
43491                Ok(Some(Expression::Subscript(Box::new(Subscript {
43492                    this: base_expr,
43493                    index: expressions.remove(0),
43494                }))))
43495            } else {
43496                // Multiple indices - create nested subscripts or array
43497                let mut result = base_expr;
43498                for expr in expressions {
43499                    result = Expression::Subscript(Box::new(Subscript {
43500                        this: result,
43501                        index: expr,
43502                    }));
43503                }
43504                Ok(Some(result))
43505            }
43506        } else {
43507            // Array literal: [1, 2, 3]
43508            Ok(Some(Expression::Array(Box::new(Array { expressions }))))
43509        }
43510    }
43511
43512    /// parse_bracket_key_value - Ported from Python _parse_bracket_key_value
43513    /// Parses key-value pairs in brackets: key: value or key => value
43514    #[allow(unused_variables, unused_mut)]
43515    pub fn parse_bracket_key_value(&mut self) -> Result<Option<Expression>> {
43516        let saved_pos = self.current;
43517
43518        // Try to parse as key: value or key => value
43519        if let Ok(key) = self.parse_primary() {
43520            // Check for : or =>
43521            if self.match_token(TokenType::Colon) || self.match_text_seq(&["=>"]) {
43522                match self.parse_expression() {
43523                    Ok(value) => {
43524                        // Return as NamedArgument for key-value pair
43525                        // Extract the name from the key (identifier or string literal)
43526                        let name = match &key {
43527                            Expression::Identifier(id) => id.clone(),
43528                            Expression::Literal(lit)
43529                                if matches!(
43530                                    lit.as_ref(),
43531                                    crate::expressions::Literal::String(s)
43532                                ) =>
43533                            {
43534                                let crate::expressions::Literal::String(s) = lit.as_ref() else {
43535                                    unreachable!()
43536                                };
43537                                Identifier::new(s.clone())
43538                            }
43539                            _ => Identifier::new("".to_string()),
43540                        };
43541                        return Ok(Some(Expression::NamedArgument(Box::new(NamedArgument {
43542                            name,
43543                            value,
43544                            separator: NamedArgSeparator::DArrow, // Using DArrow for colon-style key: value
43545                        }))));
43546                    }
43547                    Err(_) => {
43548                        self.current = saved_pos;
43549                        return Ok(None);
43550                    }
43551                }
43552            }
43553            self.current = saved_pos;
43554        }
43555
43556        Ok(None)
43557    }
43558
43559    /// parse_ceil_floor - Implemented from Python _parse_ceil_floor
43560    /// Calls: parse_lambda, parse_var
43561    #[allow(unused_variables, unused_mut)]
43562    pub fn parse_ceil_floor(&mut self) -> Result<Option<Expression>> {
43563        if self.match_text_seq(&["TO"]) {
43564            // Matched: TO
43565            return Ok(None);
43566        }
43567        Ok(None)
43568    }
43569
43570    /// parse_changes - Implemented from Python _parse_changes
43571    /// Parses: CHANGES(INFORMATION => var) AT|BEFORE(...) END(...)
43572    pub fn parse_changes(&mut self) -> Result<Option<Expression>> {
43573        // Match: CHANGES(INFORMATION =>
43574        if !self.match_text_seq(&["CHANGES", "(", "INFORMATION", "=>"]) {
43575            return Ok(None);
43576        }
43577
43578        // Parse information (any token as var)
43579        let information = self.parse_var()?.map(Box::new);
43580
43581        // Match closing paren
43582        self.match_token(TokenType::RParen);
43583
43584        // Parse at_before (Snowflake AT/BEFORE clause)
43585        let at_before = self.parse_historical_data()?.map(Box::new);
43586
43587        // Parse end (optional second historical data clause)
43588        let end = self.parse_historical_data()?.map(Box::new);
43589
43590        Ok(Some(Expression::Changes(Box::new(Changes {
43591            information,
43592            at_before,
43593            end,
43594        }))))
43595    }
43596
43597    /// parse_char - Parses CHAR/CHR function with optional USING charset
43598    /// Python: CHAR(args...) [USING charset]
43599    /// MySQL: CHAR(n1, n2, ... USING charset)
43600    pub fn parse_char(&mut self) -> Result<Option<Expression>> {
43601        // Parse expressions inside CHAR()
43602        let mut args = Vec::new();
43603        loop {
43604            let expr = self.parse_expression()?;
43605            args.push(expr);
43606            if !self.match_token(TokenType::Comma) {
43607                break;
43608            }
43609        }
43610
43611        // Check for USING charset
43612        let charset = if self.match_token(TokenType::Using) {
43613            self.parse_var()?.map(|v| {
43614                if let Expression::Identifier(id) = v {
43615                    id.name
43616                } else {
43617                    String::new()
43618                }
43619            })
43620        } else {
43621            None
43622        };
43623
43624        if args.is_empty() {
43625            return Ok(None);
43626        }
43627
43628        // If there's a charset or multiple args, use CharFunc (MySQL-style)
43629        // Otherwise use simple Chr for single-arg CHR function
43630        if charset.is_some() || args.len() > 1 {
43631            Ok(Some(Expression::CharFunc(Box::new(
43632                crate::expressions::CharFunc {
43633                    args,
43634                    charset,
43635                    name: None, // defaults to CHAR
43636                },
43637            ))))
43638        } else {
43639            Ok(Some(Expression::Chr(Box::new(UnaryFunc::new(
43640                args.into_iter().next().unwrap(),
43641            )))))
43642        }
43643    }
43644
43645    /// parse_character_set - Ported from Python _parse_character_set
43646    #[allow(unused_variables, unused_mut)]
43647    /// parse_character_set - Parses CHARACTER SET property
43648    /// Example: CHARACTER SET = utf8 or CHARACTER SET utf8mb4
43649    pub fn parse_character_set(&mut self) -> Result<Option<Expression>> {
43650        // Optional = sign
43651        self.match_token(TokenType::Eq);
43652
43653        // Parse the charset name (variable or string)
43654        let charset = self.parse_var_or_string()?;
43655        if charset.is_none() {
43656            return Ok(None);
43657        }
43658
43659        Ok(Some(Expression::CharacterSetProperty(Box::new(
43660            CharacterSetProperty {
43661                this: Box::new(charset.unwrap()),
43662                default: None,
43663            },
43664        ))))
43665    }
43666
43667    /// parse_checksum - Implemented from Python _parse_checksum
43668    #[allow(unused_variables, unused_mut)]
43669    pub fn parse_checksum(&mut self) -> Result<Option<Expression>> {
43670        if self.match_text_seq(&["OFF"]) {
43671            return Ok(Some(Expression::ChecksumProperty(Box::new(
43672                ChecksumProperty {
43673                    on: None,
43674                    default: None,
43675                },
43676            ))));
43677        }
43678        Ok(None)
43679    }
43680
43681    /// parse_cluster - CLUSTER BY clause for Hive/Spark-style queries
43682    /// Parses a list of ordered expressions (columns with optional ASC/DESC)
43683    #[allow(unused_variables, unused_mut)]
43684    pub fn parse_cluster(&mut self) -> Result<Option<Expression>> {
43685        let mut expressions: Vec<Ordered> = Vec::new();
43686
43687        loop {
43688            // Parse an ordered expression (column with optional direction)
43689            if let Some(ordered) = self.parse_ordered_item()? {
43690                expressions.push(ordered);
43691            } else {
43692                break;
43693            }
43694
43695            if !self.match_token(TokenType::Comma) {
43696                break;
43697            }
43698        }
43699
43700        if expressions.is_empty() {
43701            return Ok(None);
43702        }
43703
43704        Ok(Some(Expression::ClusterBy(Box::new(ClusterBy {
43705            expressions,
43706        }))))
43707    }
43708
43709    /// parse_clustered_by - Implemented from Python _parse_clustered_by
43710    #[allow(unused_variables, unused_mut)]
43711    pub fn parse_clustered_by(&mut self) -> Result<Option<Expression>> {
43712        if self.match_text_seq(&["BY"]) {
43713            return Ok(Some(Expression::ClusteredByProperty(Box::new(
43714                ClusteredByProperty {
43715                    expressions: Vec::new(),
43716                    sorted_by: None,
43717                    buckets: None,
43718                },
43719            ))));
43720        }
43721        if self.match_text_seq(&["SORTED", "BY"]) {
43722            // Matched: SORTED BY
43723            return Ok(None);
43724        }
43725        Ok(None)
43726    }
43727
43728    /// Parse Snowflake colon JSON path extraction: data:field or data:field.subfield
43729    /// Python: def _parse_colon_as_variant_extract(self, this)
43730    pub fn parse_colon_as_variant_extract(
43731        &mut self,
43732        this: Expression,
43733    ) -> Result<Option<Expression>> {
43734        // Build a JSON path from colon-separated identifiers
43735        // Track whether each segment was quoted (needs bracket notation for spaces/special chars)
43736        let mut json_path: Vec<(String, bool)> = Vec::new();
43737
43738        while self.match_token(TokenType::Colon) {
43739            // Parse the path segment (field name)
43740            if let Some(field) = self.parse_identifier()? {
43741                if let Expression::Identifier(ident) = field {
43742                    json_path.push((
43743                        ident.name.clone(),
43744                        ident.quoted || ident.name.contains(' ') || ident.name.contains('\''),
43745                    ));
43746                }
43747            }
43748
43749            // Check for dot-separated sub-paths
43750            while self.match_token(TokenType::Dot) {
43751                if let Some(subfield) = self.parse_identifier()? {
43752                    if let Expression::Identifier(ident) = subfield {
43753                        json_path.push((
43754                            ident.name.clone(),
43755                            ident.quoted || ident.name.contains(' ') || ident.name.contains('\''),
43756                        ));
43757                    }
43758                }
43759            }
43760        }
43761
43762        if json_path.is_empty() {
43763            return Ok(Some(this));
43764        }
43765
43766        // Build the JSON path expression string
43767        // Use bracket notation for segments with spaces/special chars: a["b c"]
43768        // Use dot notation for simple segments: a.b.c
43769        let mut path_str = String::new();
43770        for (i, (segment, needs_bracket)) in json_path.iter().enumerate() {
43771            if *needs_bracket {
43772                // Bracket notation: ["key with spaces"]
43773                path_str.push('[');
43774                path_str.push('"');
43775                path_str.push_str(segment);
43776                path_str.push('"');
43777                path_str.push(']');
43778            } else {
43779                if i > 0 {
43780                    path_str.push('.');
43781                }
43782                path_str.push_str(segment);
43783            }
43784        }
43785
43786        Ok(Some(Expression::JSONExtract(Box::new(JSONExtract {
43787            this: Box::new(this),
43788            expression: Box::new(Expression::Literal(Box::new(Literal::String(path_str)))),
43789            only_json_types: None,
43790            expressions: Vec::new(),
43791            variant_extract: Some(Box::new(Expression::Boolean(BooleanLiteral {
43792                value: true,
43793            }))),
43794            json_query: None,
43795            option: None,
43796            quote: None,
43797            on_condition: None,
43798            requires_json: None,
43799        }))))
43800    }
43801
43802    /// parse_column - Parse column expression
43803    /// Python: this = self._parse_column_reference(); return self._parse_column_ops(this)
43804    pub fn parse_column(&mut self) -> Result<Option<Expression>> {
43805        // Parse column reference (field name that becomes a Column expression)
43806        let column_ref = self.parse_column_reference()?;
43807        if column_ref.is_some() {
43808            // Apply column ops (bracket subscript, property access with dots, casts)
43809            return self.parse_column_ops_with_expr(column_ref);
43810        }
43811        // Try parsing bracket directly if no column reference
43812        self.parse_bracket()
43813    }
43814
43815    /// parse_column_constraint - Ported from Python _parse_column_constraint
43816    /// Parses column-level constraints like NOT NULL, PRIMARY KEY, UNIQUE, DEFAULT, CHECK, etc.
43817    #[allow(unused_variables, unused_mut)]
43818    pub fn parse_column_constraint(&mut self) -> Result<Option<Expression>> {
43819        // Check for optional CONSTRAINT keyword and name
43820        let constraint_name = if self.match_token(TokenType::Constraint) {
43821            self.parse_id_var()?.and_then(|e| {
43822                if let Expression::Identifier(id) = e {
43823                    Some(id)
43824                } else {
43825                    None
43826                }
43827            })
43828        } else {
43829            None
43830        };
43831
43832        // NOT NULL
43833        if self.match_text_seq(&["NOT", "NULL"]) {
43834            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
43835                NotNullColumnConstraint { allow_null: None },
43836            ))));
43837        }
43838
43839        // NOT FOR REPLICATION (SQL Server) - must be before NULL check
43840        if self.match_text_seq(&["NOT", "FOR", "REPLICATION"]) {
43841            return Ok(Some(Expression::Property(Box::new(
43842                crate::expressions::Property {
43843                    this: Box::new(Expression::Identifier(Identifier::new(
43844                        "NOT FOR REPLICATION".to_string(),
43845                    ))),
43846                    value: None,
43847                },
43848            ))));
43849        }
43850
43851        // NULL
43852        if self.match_text_seq(&["NULL"]) {
43853            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
43854                NotNullColumnConstraint {
43855                    allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
43856                        value: true,
43857                    }))),
43858                },
43859            ))));
43860        }
43861
43862        // PRIMARY KEY
43863        if self.match_text_seq(&["PRIMARY", "KEY"]) {
43864            return Ok(Some(Expression::PrimaryKeyColumnConstraint(Box::new(
43865                PrimaryKeyColumnConstraint {
43866                    desc: None,
43867                    options: Vec::new(),
43868                },
43869            ))));
43870        }
43871
43872        // UNIQUE
43873        if self.match_text_seq(&["UNIQUE"]) {
43874            // Check for optional KEY/INDEX
43875            let _ = self.match_texts(&["KEY", "INDEX"]);
43876            // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
43877            let nulls = if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
43878                Some(Box::new(Expression::Boolean(BooleanLiteral {
43879                    value: true,
43880                })))
43881            } else {
43882                None
43883            };
43884            return Ok(Some(Expression::UniqueColumnConstraint(Box::new(
43885                UniqueColumnConstraint {
43886                    this: None,
43887                    index_type: None,
43888                    on_conflict: None,
43889                    nulls,
43890                    options: Vec::new(),
43891                },
43892            ))));
43893        }
43894
43895        // DEFAULT
43896        if self.match_text_seq(&["DEFAULT"]) {
43897            let default_value = self.parse_select_or_expression()?;
43898            if let Some(val) = default_value {
43899                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
43900                    DefaultColumnConstraint {
43901                        this: Box::new(val),
43902                        for_column: None,
43903                    },
43904                ))));
43905            }
43906            return Ok(None);
43907        }
43908
43909        // CHECK
43910        if self.match_text_seq(&["CHECK"]) {
43911            if self.match_token(TokenType::LParen) {
43912                let expr = self.parse_select_or_expression()?;
43913                self.match_token(TokenType::RParen);
43914                if let Some(check_expr) = expr {
43915                    return Ok(Some(Expression::CheckColumnConstraint(Box::new(
43916                        CheckColumnConstraint {
43917                            this: Box::new(check_expr),
43918                            enforced: None,
43919                        },
43920                    ))));
43921                }
43922            }
43923            return Ok(None);
43924        }
43925
43926        // REFERENCES (foreign key)
43927        if self.match_text_seq(&["REFERENCES"]) {
43928            let table = self.parse_table_parts()?;
43929            let columns = if self.match_token(TokenType::LParen) {
43930                let mut cols = Vec::new();
43931                loop {
43932                    if let Some(col) = self.parse_id_var()? {
43933                        cols.push(col);
43934                    }
43935                    if !self.match_token(TokenType::Comma) {
43936                        break;
43937                    }
43938                }
43939                self.match_token(TokenType::RParen);
43940                cols
43941            } else {
43942                Vec::new()
43943            };
43944
43945            return Ok(Some(Expression::ForeignKey(Box::new(ForeignKey {
43946                expressions: columns,
43947                reference: table.map(Box::new),
43948                delete: None,
43949                update: None,
43950                options: Vec::new(),
43951            }))));
43952        }
43953
43954        // AUTO_INCREMENT / AUTOINCREMENT / IDENTITY
43955        if self.match_texts(&["AUTO_INCREMENT", "AUTOINCREMENT", "IDENTITY"]) {
43956            // Check for IDENTITY(start, increment) or IDENTITY START x INCREMENT y syntax
43957            let mut start = None;
43958            let mut increment = None;
43959
43960            if self.match_token(TokenType::LParen) {
43961                // Parse (start, increment)
43962                start = self.parse_bitwise()?;
43963                if self.match_token(TokenType::Comma) {
43964                    increment = self.parse_bitwise()?;
43965                }
43966                self.expect(TokenType::RParen)?;
43967            } else if self.match_text_seq(&["START"]) {
43968                // Parse START x INCREMENT y
43969                start = self.parse_bitwise()?;
43970                if self.match_text_seq(&["INCREMENT"]) {
43971                    increment = self.parse_bitwise()?;
43972                }
43973            }
43974
43975            if start.is_some() || increment.is_some() {
43976                return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
43977                    Box::new(GeneratedAsIdentityColumnConstraint {
43978                        this: Some(Box::new(Expression::Boolean(BooleanLiteral {
43979                            value: false,
43980                        }))),
43981                        expression: None,
43982                        on_null: None,
43983                        start: start.map(Box::new),
43984                        increment: increment.map(Box::new),
43985                        minvalue: None,
43986                        maxvalue: None,
43987                        cycle: None,
43988                        order: None,
43989                    }),
43990                )));
43991            }
43992            return Ok(Some(Expression::AutoIncrementColumnConstraint(
43993                AutoIncrementColumnConstraint,
43994            )));
43995        }
43996
43997        // COMMENT 'text' - CommentColumnConstraint is a unit struct, use a different expression
43998        if self.match_text_seq(&["COMMENT"]) {
43999            if let Some(comment) = self.parse_string()? {
44000                // Use CommentColumnConstraint unit struct
44001                return Ok(Some(Expression::CommentColumnConstraint(
44002                    CommentColumnConstraint,
44003                )));
44004            }
44005            return Ok(None);
44006        }
44007
44008        // COLLATE collation_name - use CollateProperty instead
44009        if self.match_text_seq(&["COLLATE"]) {
44010            if let Some(collation) = self.parse_id_var()? {
44011                return Ok(Some(Expression::CollateProperty(Box::new(
44012                    CollateProperty {
44013                        this: Box::new(collation),
44014                        default: None,
44015                    },
44016                ))));
44017            }
44018            return Ok(None);
44019        }
44020
44021        // ClickHouse dictionary column attributes: HIERARCHICAL, IS_OBJECT_ID, INJECTIVE
44022        if matches!(
44023            self.config.dialect,
44024            Some(crate::dialects::DialectType::ClickHouse)
44025        ) {
44026            if self.match_texts(&["HIERARCHICAL", "IS_OBJECT_ID", "INJECTIVE"]) {
44027                let attr_name = self.previous().text.to_ascii_uppercase();
44028                return Ok(Some(Expression::Property(Box::new(
44029                    crate::expressions::Property {
44030                        this: Box::new(Expression::Identifier(Identifier::new(attr_name))),
44031                        value: None,
44032                    },
44033                ))));
44034            }
44035            // ClickHouse EXPRESSION expr and ALIAS expr (dictionary column attributes)
44036            if self.match_texts(&["EXPRESSION"]) {
44037                let expr = self.parse_expression()?;
44038                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
44039                    DefaultColumnConstraint {
44040                        this: Box::new(expr),
44041                        for_column: None,
44042                    },
44043                ))));
44044            }
44045        }
44046
44047        // GENERATED ... AS IDENTITY
44048        if self.match_text_seq(&["GENERATED"]) {
44049            let always = self.match_text_seq(&["ALWAYS"]);
44050            if !always {
44051                self.match_text_seq(&["BY", "DEFAULT"]);
44052            }
44053            let on_null = self.match_text_seq(&["ON", "NULL"]);
44054            if self.match_text_seq(&["AS", "IDENTITY"]) {
44055                return Ok(Some(Expression::GeneratedAsIdentityColumnConstraint(
44056                    Box::new(GeneratedAsIdentityColumnConstraint {
44057                        this: None,
44058                        expression: None,
44059                        on_null: if on_null {
44060                            Some(Box::new(Expression::Boolean(BooleanLiteral {
44061                                value: true,
44062                            })))
44063                        } else {
44064                            None
44065                        },
44066                        start: None,
44067                        increment: None,
44068                        minvalue: None,
44069                        maxvalue: None,
44070                        cycle: None,
44071                        order: None,
44072                    }),
44073                )));
44074            }
44075            return Ok(None);
44076        }
44077
44078        // PATH 'xpath' - for XMLTABLE/JSON_TABLE columns
44079        if self.match_text_seq(&["PATH"]) {
44080            if let Some(path_expr) = self.parse_string()? {
44081                return Ok(Some(Expression::PathColumnConstraint(Box::new(
44082                    PathColumnConstraint {
44083                        this: Box::new(path_expr),
44084                    },
44085                ))));
44086            }
44087            return Ok(None);
44088        }
44089
44090        // Return the constraint name if we matched CONSTRAINT but no actual constraint
44091        if let Some(name) = constraint_name {
44092            return Ok(Some(Expression::Identifier(name)));
44093        }
44094
44095        Ok(None)
44096    }
44097
44098    /// parse_column_def_with_exists - Ported from Python _parse_column_def_with_exists
44099    /// Parses a column definition with optional IF [NOT] EXISTS clause
44100    #[allow(unused_variables, unused_mut)]
44101    pub fn parse_column_def_with_exists(&mut self) -> Result<Option<Expression>> {
44102        let start = self.current;
44103
44104        // Optionally match COLUMN keyword
44105        let _ = self.match_text_seq(&["COLUMN"]);
44106
44107        // Check for IF NOT EXISTS
44108        let not_exists = self.match_text_seq(&["IF", "NOT", "EXISTS"]);
44109        let exists = if !not_exists {
44110            self.match_text_seq(&["IF", "EXISTS"])
44111        } else {
44112            false
44113        };
44114
44115        // Parse the field definition
44116        let expression = self.parse_field_def()?;
44117
44118        if expression.is_none() {
44119            self.current = start;
44120            return Ok(None);
44121        }
44122
44123        // If it's a ColumnDef, we're good
44124        if let Some(Expression::ColumnDef(ref _col_def)) = expression {
44125            // The exists flag would be set on the ColumnDef, but our struct doesn't have that field
44126            // Just return the expression as-is
44127            return Ok(expression);
44128        }
44129
44130        // Not a ColumnDef, backtrack
44131        self.current = start;
44132        Ok(None)
44133    }
44134
44135    /// parse_column_ops - Parses column operations (stub for compatibility)
44136    pub fn parse_column_ops(&mut self) -> Result<Option<Expression>> {
44137        self.parse_column_ops_with_expr(None)
44138    }
44139
44140    /// parse_column_ops_with_expr - Parses column operations (dot access, brackets, casts)
44141    /// Python: _parse_column_ops(this)
44142    pub fn parse_column_ops_with_expr(
44143        &mut self,
44144        this: Option<Expression>,
44145    ) -> Result<Option<Expression>> {
44146        // First apply any bracket subscripts
44147        let mut result = if let Some(expr) = this {
44148            if self.match_token(TokenType::LBracket) {
44149                let index = self.parse_disjunction()?;
44150                self.match_token(TokenType::RBracket);
44151                if let Some(idx) = index {
44152                    Some(Expression::Subscript(Box::new(Subscript {
44153                        this: expr,
44154                        index: idx,
44155                    })))
44156                } else {
44157                    Some(expr)
44158                }
44159            } else {
44160                Some(expr)
44161            }
44162        } else {
44163            None
44164        };
44165
44166        // Handle DOT for qualified column names: table.column or schema.table.column
44167        while self.match_token(TokenType::Dot) {
44168            if result.is_none() {
44169                break;
44170            }
44171            // Handle .* (qualified star) with modifiers
44172            if self.match_token(TokenType::Star) {
44173                // Determine table name from the expression
44174                let table_name = match &result {
44175                    Some(Expression::Column(col)) if col.table.is_none() => Some(col.name.clone()),
44176                    Some(Expression::Dot(dot)) => {
44177                        // For deep qualified names like schema.table.*, use the whole expression name
44178                        fn dot_to_name(expr: &Expression) -> String {
44179                            match expr {
44180                                Expression::Column(col) => {
44181                                    if let Some(ref table) = col.table {
44182                                        format!("{}.{}", table.name, col.name.name)
44183                                    } else {
44184                                        col.name.name.clone()
44185                                    }
44186                                }
44187                                Expression::Dot(d) => {
44188                                    format!("{}.{}", dot_to_name(&d.this), d.field.name)
44189                                }
44190                                _ => String::new(),
44191                            }
44192                        }
44193                        Some(Identifier::new(dot_to_name(&Expression::Dot(dot.clone()))))
44194                    }
44195                    _ => None,
44196                };
44197                let star = self.parse_star_modifiers(table_name)?;
44198                result = Some(Expression::Star(star));
44199                break;
44200            }
44201            // Parse the field identifier - use is_identifier_or_keyword_token to allow keywords
44202            // like "schema" as field names in dot access
44203            // ClickHouse: also allow numeric tuple index access like expr.1, expr.2
44204            if self.is_identifier_or_keyword_token()
44205                || self.check(TokenType::QuotedIdentifier)
44206                || (matches!(
44207                    self.config.dialect,
44208                    Some(crate::dialects::DialectType::ClickHouse)
44209                ) && self.check(TokenType::Number))
44210            {
44211                let token = self.advance();
44212                let field_ident = Identifier {
44213                    name: token.text,
44214                    quoted: token.token_type == TokenType::QuotedIdentifier,
44215                    trailing_comments: Vec::new(),
44216                    span: None,
44217                };
44218                result = Some(Expression::Dot(Box::new(DotAccess {
44219                    this: result.take().unwrap(),
44220                    field: field_ident,
44221                })));
44222            } else {
44223                break;
44224            }
44225        }
44226
44227        // Handle EXCLAMATION for Snowflake model attribute syntax: model!PREDICT(...)
44228        if self.match_token(TokenType::Exclamation) {
44229            if let Some(expr) = result.take() {
44230                // Parse the attribute/function after the exclamation mark
44231                // This can be either a simple identifier (model!admin) or a function call (model!PREDICT(1))
44232                let attr = self.parse_unary()?;
44233                result = Some(Expression::ModelAttribute(Box::new(ModelAttribute {
44234                    this: Box::new(expr),
44235                    expression: Box::new(attr),
44236                })));
44237            }
44238        }
44239
44240        // Handle DCOLON for casts (PostgreSQL syntax: column::type)
44241        if self.match_token(TokenType::DColon) {
44242            if let Some(type_expr) = self.parse_types()? {
44243                if let Some(expr) = result {
44244                    // Extract DataType from the expression
44245                    let data_type = match type_expr {
44246                        Expression::DataType(dt) => dt,
44247                        _ => {
44248                            result = Some(expr);
44249                            return Ok(result);
44250                        }
44251                    };
44252                    result = Some(Expression::Cast(Box::new(Cast {
44253                        this: expr,
44254                        to: data_type,
44255                        trailing_comments: Vec::new(),
44256                        double_colon_syntax: true,
44257                        format: None,
44258                        default: None,
44259                        inferred_type: None,
44260                    })));
44261                }
44262            }
44263        }
44264
44265        // Teradata: (FORMAT '...') phrase after a column/expression
44266        if matches!(
44267            self.config.dialect,
44268            Some(crate::dialects::DialectType::Teradata)
44269        ) && self.check(TokenType::LParen)
44270            && self.check_next(TokenType::Format)
44271        {
44272            self.skip(); // consume (
44273            self.skip(); // consume FORMAT
44274            let format = self.expect_string()?;
44275            self.expect(TokenType::RParen)?;
44276            if let Some(expr) = result.take() {
44277                result = Some(Expression::FormatPhrase(Box::new(FormatPhrase {
44278                    this: Box::new(expr),
44279                    format,
44280                })));
44281            }
44282        }
44283
44284        Ok(result)
44285    }
44286
44287    /// parse_column_reference - Parse column reference (field -> Column)
44288    /// Python: this = self._parse_field(); if isinstance(this, exp.Identifier): return exp.Column(this=this)
44289    pub fn parse_column_reference(&mut self) -> Result<Option<Expression>> {
44290        // Parse the field (identifier or literal)
44291        if let Some(field) = self.parse_field()? {
44292            // If it's an identifier, wrap it in a Column expression
44293            match &field {
44294                Expression::Identifier(id) => {
44295                    return Ok(Some(Expression::boxed_column(Column {
44296                        name: id.clone(),
44297                        table: None,
44298                        join_mark: false,
44299                        trailing_comments: Vec::new(),
44300                        span: None,
44301                        inferred_type: None,
44302                    })));
44303                }
44304                // If it's already something else (like a literal), return as-is
44305                _ => return Ok(Some(field)),
44306            }
44307        }
44308        Ok(None)
44309    }
44310
44311    /// parse_command - Parses a generic SQL command
44312    /// Python: _parse_command
44313    /// Used for commands that we don't have specific parsing for
44314    pub fn parse_command(&mut self) -> Result<Option<Expression>> {
44315        // Get the command keyword from the previous token
44316        let command_text = self.previous().text.to_ascii_uppercase();
44317
44318        // Collect remaining tokens as the command expression (until statement end)
44319        // Use (text, token_type) tuples for smart spacing with join_command_tokens
44320        let mut tokens: Vec<(String, TokenType)> = vec![(command_text, TokenType::Var)];
44321        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
44322            let token = self.advance();
44323            // Preserve quotes for quoted identifiers and strings
44324            let text = if token.token_type == TokenType::QuotedIdentifier {
44325                // Re-add the identifier quote characters
44326                // Use backticks as default; this handles MySQL backtick-quoted identifiers
44327                // and double-quoted identifiers for other dialects
44328                let quote_char = if self.config.dialect == Some(crate::dialects::DialectType::MySQL)
44329                    || self.config.dialect == Some(crate::dialects::DialectType::SingleStore)
44330                    || self.config.dialect == Some(crate::dialects::DialectType::Doris)
44331                    || self.config.dialect == Some(crate::dialects::DialectType::StarRocks)
44332                {
44333                    '`'
44334                } else {
44335                    '"'
44336                };
44337                format!("{}{}{}", quote_char, token.text, quote_char)
44338            } else if token.token_type == TokenType::String {
44339                format!("'{}'", token.text)
44340            } else {
44341                token.text.clone()
44342            };
44343            tokens.push((text, token.token_type));
44344        }
44345
44346        Ok(Some(Expression::Command(Box::new(Command {
44347            this: self.join_command_tokens(tokens),
44348        }))))
44349    }
44350
44351    /// parse_commit_or_rollback - Implemented from Python _parse_commit_or_rollback
44352    #[allow(unused_variables, unused_mut)]
44353    pub fn parse_commit_or_rollback(&mut self) -> Result<Option<Expression>> {
44354        if self.match_text_seq(&["TO"]) {
44355            return Ok(Some(Expression::Rollback(Box::new(Rollback {
44356                savepoint: None,
44357                this: None,
44358            }))));
44359        }
44360        if self.match_text_seq(&["SAVEPOINT"]) {
44361            // Matched: SAVEPOINT
44362            return Ok(None);
44363        }
44364        Ok(None)
44365    }
44366
44367    /// parse_composite_key_property - Implemented from Python _parse_composite_key_property
44368    #[allow(unused_variables, unused_mut)]
44369    pub fn parse_composite_key_property(&mut self) -> Result<Option<Expression>> {
44370        if self.match_text_seq(&["KEY"]) {
44371            // Matched: KEY
44372            return Ok(None);
44373        }
44374        Ok(None)
44375    }
44376
44377    /// parse_comprehension - Implemented from Python _parse_comprehension
44378    /// Parses list comprehension: expr FOR var [, position] IN iterator [IF condition]
44379    pub fn parse_comprehension(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
44380        let start_index = self.current;
44381
44382        // Parse expression (column)
44383        let expression = self.parse_column()?;
44384
44385        // Parse optional position (if comma follows)
44386        let position = if self.match_token(TokenType::Comma) {
44387            self.parse_column()?.map(Box::new)
44388        } else {
44389            None
44390        };
44391
44392        // Must have IN keyword
44393        if !self.match_token(TokenType::In) {
44394            // Backtrack
44395            self.current = start_index.saturating_sub(1);
44396            return Ok(None);
44397        }
44398
44399        // Parse iterator
44400        let iterator = self.parse_column()?.map(Box::new);
44401
44402        // Parse optional condition (IF followed by expression)
44403        let condition = if self.match_text_seq(&["IF"]) {
44404            self.parse_disjunction()?.map(Box::new)
44405        } else {
44406            None
44407        };
44408
44409        // Build the comprehension expression
44410        match (this, expression) {
44411            (Some(t), Some(e)) => Ok(Some(Expression::Comprehension(Box::new(Comprehension {
44412                this: Box::new(t),
44413                expression: Box::new(e),
44414                position,
44415                iterator,
44416                condition,
44417            })))),
44418            _ => Ok(None),
44419        }
44420    }
44421
44422    /// parse_compress - Parses COMPRESS column constraint (Teradata)
44423    /// Python: _parse_compress
44424    /// Format: COMPRESS or COMPRESS (value1, value2, ...)
44425    pub fn parse_compress(&mut self) -> Result<Option<Expression>> {
44426        // Check if it's a parenthesized list of values
44427        if self.check(TokenType::LParen) {
44428            // Parse wrapped CSV of bitwise expressions
44429            self.skip(); // consume LParen
44430            let mut expressions = Vec::new();
44431            loop {
44432                if let Some(expr) = self.parse_bitwise()? {
44433                    expressions.push(expr);
44434                } else {
44435                    break;
44436                }
44437                if !self.match_token(TokenType::Comma) {
44438                    break;
44439                }
44440            }
44441            self.expect(TokenType::RParen)?;
44442
44443            // Wrap in a Tuple if multiple values
44444            let this = if expressions.len() == 1 {
44445                Some(Box::new(expressions.into_iter().next().unwrap()))
44446            } else if expressions.is_empty() {
44447                None
44448            } else {
44449                Some(Box::new(Expression::Tuple(Box::new(Tuple { expressions }))))
44450            };
44451
44452            Ok(Some(Expression::CompressColumnConstraint(Box::new(
44453                CompressColumnConstraint { this },
44454            ))))
44455        } else {
44456            // Single value or no value
44457            let this = self.parse_bitwise()?.map(Box::new);
44458            Ok(Some(Expression::CompressColumnConstraint(Box::new(
44459                CompressColumnConstraint { this },
44460            ))))
44461        }
44462    }
44463
44464    /// parse_conjunction - Parses AND expressions
44465    /// Python: _parse_conjunction
44466    /// Delegates to the existing parse_and in the operator precedence chain
44467    pub fn parse_conjunction(&mut self) -> Result<Option<Expression>> {
44468        match self.parse_and() {
44469            Ok(expr) => Ok(Some(expr)),
44470            Err(_) => Ok(None),
44471        }
44472    }
44473
44474    /// parse_connect_with_prior - Parses expression in CONNECT BY context with PRIOR support
44475    /// Python: _parse_connect_with_prior
44476    /// This method temporarily treats PRIOR as a prefix operator while parsing the expression
44477    pub fn parse_connect_with_prior(&mut self) -> Result<Option<Expression>> {
44478        // parse_connect_expression already handles PRIOR as a prefix operator
44479        let connect = self.parse_connect_expression()?;
44480        Ok(Some(connect))
44481    }
44482
44483    /// parse_constraint - Parses named or unnamed constraint
44484    /// Python: _parse_constraint
44485    pub fn parse_constraint(&mut self) -> Result<Option<Expression>> {
44486        // Check for CONSTRAINT keyword (named constraint)
44487        if !self.match_token(TokenType::Constraint) {
44488            // Try to parse an unnamed constraint
44489            return self.parse_unnamed_constraint();
44490        }
44491
44492        // Parse the constraint name
44493        let name = self.parse_id_var()?;
44494        if name.is_none() {
44495            return Ok(None);
44496        }
44497
44498        // Parse the constraint expressions (PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK, etc.)
44499        let expressions = self.parse_unnamed_constraints()?;
44500
44501        Ok(Some(Expression::Constraint(Box::new(Constraint {
44502            this: Box::new(name.unwrap()),
44503            expressions,
44504        }))))
44505    }
44506
44507    /// parse_unnamed_constraints - Parses multiple unnamed constraints
44508    /// Python: _parse_unnamed_constraints
44509    pub fn parse_unnamed_constraints(&mut self) -> Result<Vec<Expression>> {
44510        let mut constraints = Vec::new();
44511
44512        loop {
44513            if let Some(constraint) = self.parse_unnamed_constraint()? {
44514                constraints.push(constraint);
44515            } else {
44516                break;
44517            }
44518        }
44519
44520        Ok(constraints)
44521    }
44522
44523    /// parse_unnamed_constraint - Parses a single unnamed constraint
44524    /// Python: _parse_unnamed_constraint
44525    pub fn parse_unnamed_constraint(&mut self) -> Result<Option<Expression>> {
44526        // Try PRIMARY KEY
44527        if self.match_text_seq(&["PRIMARY", "KEY"]) {
44528            // ClickHouse: PRIMARY KEY expr (without parens) in schema = table-level PK expression
44529            if matches!(
44530                self.config.dialect,
44531                Some(crate::dialects::DialectType::ClickHouse)
44532            ) && !self.check(TokenType::LParen)
44533            {
44534                let expr = self.parse_expression()?;
44535                return Ok(Some(Expression::Raw(Raw {
44536                    sql: format!("PRIMARY KEY {}", expr),
44537                })));
44538            }
44539            return self.parse_primary_key();
44540        }
44541
44542        // Try UNIQUE
44543        if self.match_texts(&["UNIQUE"]) {
44544            return self.parse_unique();
44545        }
44546
44547        // Try FOREIGN KEY
44548        if self.match_text_seq(&["FOREIGN", "KEY"]) {
44549            return self.parse_foreign_key();
44550        }
44551
44552        // Try CHECK
44553        if self.match_texts(&["CHECK"]) {
44554            let expr = self.parse_wrapped()?;
44555            if let Some(check_expr) = expr {
44556                return Ok(Some(Expression::CheckColumnConstraint(Box::new(
44557                    CheckColumnConstraint {
44558                        this: Box::new(check_expr),
44559                        enforced: None,
44560                    },
44561                ))));
44562            }
44563        }
44564
44565        // Try NOT NULL
44566        if self.match_text_seq(&["NOT", "NULL"]) {
44567            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
44568                NotNullColumnConstraint {
44569                    allow_null: None, // NOT NULL means allow_null is not set
44570                },
44571            ))));
44572        }
44573
44574        // Try NULL (allow null)
44575        if self.match_texts(&["NULL"]) {
44576            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
44577                NotNullColumnConstraint {
44578                    allow_null: Some(Box::new(Expression::Boolean(BooleanLiteral {
44579                        value: true,
44580                    }))),
44581                },
44582            ))));
44583        }
44584
44585        // Try DEFAULT
44586        if self.match_token(TokenType::Default) {
44587            let default_value = self.parse_bitwise()?;
44588            if let Some(val) = default_value {
44589                // TSQL: DEFAULT value FOR column (table-level default constraint)
44590                let for_column = if self.match_token(TokenType::For) {
44591                    Some(self.expect_identifier_with_quoted()?)
44592                } else {
44593                    None
44594                };
44595                return Ok(Some(Expression::DefaultColumnConstraint(Box::new(
44596                    DefaultColumnConstraint {
44597                        this: Box::new(val),
44598                        for_column,
44599                    },
44600                ))));
44601            }
44602        }
44603
44604        // Try REFERENCES (inline foreign key)
44605        if self.match_texts(&["REFERENCES"]) {
44606            return self.parse_references();
44607        }
44608
44609        // ClickHouse: INDEX name expr TYPE type_name [GRANULARITY n]
44610        if matches!(
44611            self.config.dialect,
44612            Some(crate::dialects::DialectType::ClickHouse)
44613        ) && self.match_token(TokenType::Index)
44614        {
44615            let name = self.expect_identifier_or_keyword_with_quoted()?;
44616            // Use parse_conjunction to handle comparisons like c0 < (SELECT _table)
44617            let expression = self.parse_conjunction()?.ok_or_else(|| {
44618                self.parse_error("Expected expression in ClickHouse INDEX definition")
44619            })?;
44620            let index_type = if self.match_token(TokenType::Type) {
44621                if let Some(func) = self.parse_function()? {
44622                    Some(Box::new(func))
44623                } else if !self.is_at_end() {
44624                    let type_name = self.advance().text.clone();
44625                    if self.check(TokenType::LParen) {
44626                        self.skip();
44627                        let mut args = Vec::new();
44628                        if !self.check(TokenType::RParen) {
44629                            args.push(self.parse_expression()?);
44630                            while self.match_token(TokenType::Comma) {
44631                                args.push(self.parse_expression()?);
44632                            }
44633                        }
44634                        self.expect(TokenType::RParen)?;
44635                        Some(Box::new(Expression::Function(Box::new(Function::new(
44636                            type_name, args,
44637                        )))))
44638                    } else {
44639                        Some(Box::new(Expression::Identifier(Identifier::new(type_name))))
44640                    }
44641                } else {
44642                    None
44643                }
44644            } else {
44645                None
44646            };
44647            let _granularity = if self.match_identifier("GRANULARITY") {
44648                let _ = self.parse_expression()?;
44649                true
44650            } else {
44651                false
44652            };
44653            // Return as a raw SQL expression preserving the INDEX definition
44654            let mut sql = format!("INDEX {} ", name.name);
44655            if let Some(ref idx_type) = index_type {
44656                sql.push_str(&format!("{} TYPE {} ", expression, idx_type));
44657            }
44658            return Ok(Some(Expression::Raw(Raw {
44659                sql: sql.trim().to_string(),
44660            })));
44661        }
44662
44663        // ClickHouse: PROJECTION name (SELECT ...) or PROJECTION name INDEX expr TYPE type_name
44664        if matches!(
44665            self.config.dialect,
44666            Some(crate::dialects::DialectType::ClickHouse)
44667        ) && self.check_identifier("PROJECTION")
44668        {
44669            self.skip(); // consume PROJECTION
44670            let name = self.expect_identifier_or_keyword_with_quoted()?;
44671            // Parse the projection body - either (SELECT ...) or INDEX expr TYPE type_name
44672            if self.match_token(TokenType::LParen) {
44673                let mut depth = 1i32;
44674                let start = self.current;
44675                while !self.is_at_end() && depth > 0 {
44676                    if self.check(TokenType::LParen) {
44677                        depth += 1;
44678                    }
44679                    if self.check(TokenType::RParen) {
44680                        depth -= 1;
44681                        if depth == 0 {
44682                            break;
44683                        }
44684                    }
44685                    self.skip();
44686                }
44687                let body_sql = self.tokens_to_sql(start, self.current);
44688                self.expect(TokenType::RParen)?;
44689                return Ok(Some(Expression::Raw(Raw {
44690                    sql: format!("PROJECTION {} ({})", name.name, body_sql),
44691                })));
44692            }
44693            // PROJECTION name INDEX expr TYPE type_name
44694            if self.match_token(TokenType::Index) {
44695                let expr = self.parse_bitwise()?.ok_or_else(|| {
44696                    self.parse_error(
44697                        "Expected expression in ClickHouse PROJECTION INDEX definition",
44698                    )
44699                })?;
44700                let type_str = if self.match_token(TokenType::Type) {
44701                    if !self.is_at_end() {
44702                        let t = self.advance().text.clone();
44703                        format!(" TYPE {}", t)
44704                    } else {
44705                        String::new()
44706                    }
44707                } else {
44708                    String::new()
44709                };
44710                return Ok(Some(Expression::Raw(Raw {
44711                    sql: format!("PROJECTION {} INDEX {}{}", name.name, expr, type_str),
44712                })));
44713            }
44714            return Ok(Some(Expression::Raw(Raw {
44715                sql: format!("PROJECTION {}", name.name),
44716            })));
44717        }
44718
44719        Ok(None)
44720    }
44721
44722    /// parse_contains_property - Implemented from Python _parse_contains_property
44723    #[allow(unused_variables, unused_mut)]
44724    pub fn parse_contains_property(&mut self) -> Result<Option<Expression>> {
44725        if self.match_text_seq(&["SQL"]) {
44726            // Matched: SQL
44727            return Ok(None);
44728        }
44729        Ok(None)
44730    }
44731
44732    /// parse_convert - Ported from Python _parse_convert
44733    /// Parses CONVERT function: CONVERT(expr USING charset) or CONVERT(expr, type)
44734    #[allow(unused_variables, unused_mut)]
44735    pub fn parse_convert(&mut self) -> Result<Option<Expression>> {
44736        // Parse the expression to convert
44737        let this = match self.parse_bitwise() {
44738            Ok(Some(expr)) => expr,
44739            Ok(None) => return Ok(None),
44740            Err(e) => return Err(e),
44741        };
44742
44743        // Check for USING charset (CONVERT(x USING utf8))
44744        if self.match_token(TokenType::Using) {
44745            let _ = self.parse_var(); // charset
44746                                      // Return as Cast with charset
44747            return Ok(Some(Expression::Cast(Box::new(Cast {
44748                this,
44749                to: DataType::Char { length: None },
44750                trailing_comments: Vec::new(),
44751                double_colon_syntax: false,
44752                format: None,
44753                default: None,
44754                inferred_type: None,
44755            }))));
44756        }
44757
44758        // Check for comma then type (CONVERT(x, INT))
44759        if self.match_token(TokenType::Comma) {
44760            let data_type = self.parse_data_type()?;
44761            return Ok(Some(Expression::Cast(Box::new(Cast {
44762                this,
44763                to: data_type,
44764                trailing_comments: Vec::new(),
44765                double_colon_syntax: false,
44766                format: None,
44767                default: None,
44768                inferred_type: None,
44769            }))));
44770        }
44771
44772        // No type specified, return as-is wrapped in Cast
44773        Ok(Some(Expression::Cast(Box::new(Cast {
44774            this,
44775            to: DataType::Char { length: None },
44776            trailing_comments: Vec::new(),
44777            double_colon_syntax: false,
44778            format: None,
44779            default: None,
44780            inferred_type: None,
44781        }))))
44782    }
44783
44784    /// parse_copy_parameters - Implemented from Python _parse_copy_parameters
44785    /// parse_copy_parameters - Parses COPY statement parameters
44786    /// Returns a tuple of CopyParameter expressions
44787    pub fn parse_copy_parameters(&mut self) -> Result<Option<Expression>> {
44788        let mut options = Vec::new();
44789
44790        while !self.is_at_end() && !self.check(TokenType::RParen) {
44791            // Parse option name as var
44792            let option = self.parse_var()?;
44793            if option.is_none() {
44794                break;
44795            }
44796
44797            let option_name = match &option {
44798                Some(Expression::Var(v)) => v.this.to_ascii_uppercase(),
44799                Some(Expression::Identifier(id)) => id.name.to_ascii_uppercase(),
44800                _ => String::new(),
44801            };
44802
44803            // Options and values may be separated by whitespace, "=" or "AS"
44804            self.match_token(TokenType::Eq);
44805            self.match_token(TokenType::Alias);
44806
44807            // Parse value based on option type
44808            let (expression, expressions) = if (option_name == "FILE_FORMAT"
44809                || option_name == "FORMAT_OPTIONS")
44810                && self.check(TokenType::LParen)
44811            {
44812                // Parse wrapped options for FILE_FORMAT
44813                let wrapped = self.parse_wrapped_options()?;
44814                let exprs = match wrapped {
44815                    Some(Expression::Tuple(t)) => t.expressions,
44816                    Some(e) => vec![e],
44817                    None => Vec::new(),
44818                };
44819                (None, exprs)
44820            } else if option_name == "FILE_FORMAT" {
44821                // T-SQL external file format case
44822                let field = self.parse_field()?;
44823                (field, Vec::new())
44824            } else if option_name == "FORMAT"
44825                && self.previous().token_type == TokenType::Alias
44826                && self.match_texts(&["AVRO", "JSON"])
44827            {
44828                // FORMAT AS AVRO/JSON
44829                let format_type = self.previous().text.to_ascii_uppercase();
44830                let field = self.parse_field()?;
44831                (
44832                    Some(Expression::Var(Box::new(Var {
44833                        this: format!("FORMAT AS {}", format_type),
44834                    }))),
44835                    field.map_or(Vec::new(), |f| vec![f]),
44836                )
44837            } else {
44838                // Parse unquoted field or bracket
44839                let expr = self
44840                    .parse_unquoted_field()?
44841                    .or_else(|| self.parse_bracket().ok().flatten());
44842                (expr, Vec::new())
44843            };
44844
44845            options.push(Expression::CopyParameter(Box::new(CopyParameter {
44846                name: option_name,
44847                value: expression,
44848                values: expressions,
44849                eq: true,
44850            })));
44851
44852            // Optional comma separator (dialect-specific)
44853            self.match_token(TokenType::Comma);
44854        }
44855
44856        if options.is_empty() {
44857            Ok(None)
44858        } else {
44859            Ok(Some(Expression::Tuple(Box::new(Tuple {
44860                expressions: options,
44861            }))))
44862        }
44863    }
44864
44865    /// parse_copy_property - Implemented from Python _parse_copy_property
44866    #[allow(unused_variables, unused_mut)]
44867    pub fn parse_copy_property(&mut self) -> Result<Option<Expression>> {
44868        if self.match_text_seq(&["GRANTS"]) {
44869            // Matched: GRANTS
44870            return Ok(None);
44871        }
44872        Ok(None)
44873    }
44874
44875    /// parse_create_like - Implemented from Python _parse_create_like
44876    /// Calls: parse_id_var
44877    #[allow(unused_variables, unused_mut)]
44878    pub fn parse_create_like(&mut self) -> Result<Option<Expression>> {
44879        if self.match_texts(&["INCLUDING", "EXCLUDING"]) {
44880            // Matched one of: INCLUDING, EXCLUDING
44881            return Ok(None);
44882        }
44883        Ok(None)
44884    }
44885
44886    /// parse_credentials - Implemented from Python _parse_credentials
44887    #[allow(unused_variables, unused_mut)]
44888    pub fn parse_credentials(&mut self) -> Result<Option<Expression>> {
44889        if self.match_text_seq(&["STORAGE_INTEGRATION", "="]) {
44890            return Ok(Some(Expression::Credentials(Box::new(Credentials {
44891                credentials: Vec::new(),
44892                encryption: None,
44893                storage: None,
44894            }))));
44895        }
44896        if self.match_text_seq(&["CREDENTIALS"]) {
44897            // Matched: CREDENTIALS
44898            return Ok(None);
44899        }
44900        Ok(None)
44901    }
44902
44903    /// parse_csv - Parses comma-separated expressions
44904    /// Python: _parse_csv
44905    /// In Python this takes a parse_method callback, but in Rust we use parse_expression_list
44906    pub fn parse_csv(&mut self) -> Result<Option<Expression>> {
44907        let expressions = self.parse_expression_list()?;
44908        if expressions.is_empty() {
44909            return Ok(None);
44910        }
44911        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
44912    }
44913
44914    /// parse_cte - Implemented from Python _parse_cte
44915    /// Calls: parse_wrapped_id_vars
44916    #[allow(unused_variables, unused_mut)]
44917    pub fn parse_cte(&mut self) -> Result<Option<Expression>> {
44918        if self.match_text_seq(&["USING", "KEY"]) {
44919            return Ok(Some(Expression::Values(Box::new(Values {
44920                expressions: Vec::new(),
44921                alias: None,
44922                column_aliases: Vec::new(),
44923            }))));
44924        }
44925        if self.match_text_seq(&["NOT", "MATERIALIZED"]) {
44926            // Matched: NOT MATERIALIZED
44927            return Ok(None);
44928        }
44929        if self.match_text_seq(&["MATERIALIZED"]) {
44930            // Matched: MATERIALIZED
44931            return Ok(None);
44932        }
44933        Ok(None)
44934    }
44935
44936    /// parse_cube_or_rollup - Ported from Python _parse_cube_or_rollup
44937    /// Parses CUBE(...) or ROLLUP(...) expressions in GROUP BY
44938    #[allow(unused_variables, unused_mut)]
44939    pub fn parse_cube_or_rollup(&mut self) -> Result<Option<Expression>> {
44940        // Check for CUBE or ROLLUP keyword
44941        let is_cube = self.match_texts(&["CUBE"]);
44942        let is_rollup = if !is_cube {
44943            self.match_texts(&["ROLLUP"])
44944        } else {
44945            false
44946        };
44947
44948        if !is_cube && !is_rollup {
44949            return Ok(None);
44950        }
44951
44952        // Parse wrapped expressions
44953        self.expect(TokenType::LParen)?;
44954        let mut expressions = Vec::new();
44955        if !self.check(TokenType::RParen) {
44956            loop {
44957                match self.parse_bitwise() {
44958                    Ok(Some(expr)) => expressions.push(expr),
44959                    Ok(None) => break,
44960                    Err(e) => return Err(e),
44961                }
44962                if !self.match_token(TokenType::Comma) {
44963                    break;
44964                }
44965            }
44966        }
44967        self.expect(TokenType::RParen)?;
44968
44969        if is_cube {
44970            Ok(Some(Expression::Cube(Box::new(Cube { expressions }))))
44971        } else {
44972            Ok(Some(Expression::Rollup(Box::new(Rollup { expressions }))))
44973        }
44974    }
44975
44976    /// parse_data_deletion_property - Implemented from Python _parse_data_deletion_property
44977    /// Calls: parse_column, parse_retention_period
44978    #[allow(unused_variables, unused_mut)]
44979    pub fn parse_data_deletion_property(&mut self) -> Result<Option<Expression>> {
44980        if self.match_text_seq(&["ON"]) {
44981            // Matched: ON
44982            return Ok(None);
44983        }
44984        if self.match_text_seq(&["OFF"]) {
44985            // Matched: OFF
44986            return Ok(None);
44987        }
44988        if self.match_text_seq(&["FILTER_COLUMN", "="]) {
44989            // Matched: FILTER_COLUMN =
44990            return Ok(None);
44991        }
44992        Ok(None)
44993    }
44994
44995    /// parse_datablocksize - Implemented from Python _parse_datablocksize
44996    /// Calls: parse_number
44997    #[allow(unused_variables, unused_mut)]
44998    pub fn parse_datablocksize(&mut self) -> Result<Option<Expression>> {
44999        if self.match_texts(&["BYTES", "KBYTES", "KILOBYTES"]) {
45000            // Matched one of: BYTES, KBYTES, KILOBYTES
45001            return Ok(None);
45002        }
45003        Ok(None)
45004    }
45005
45006    /// parse_dcolon - Delegates to parse_types
45007    #[allow(unused_variables, unused_mut)]
45008    pub fn parse_dcolon(&mut self) -> Result<Option<Expression>> {
45009        self.parse_types()
45010    }
45011
45012    /// parse_ddl_select - Ported from Python _parse_ddl_select
45013    /// Parses a SELECT statement in DDL context (CREATE TABLE AS SELECT, INSERT INTO ... SELECT)
45014    #[allow(unused_variables, unused_mut)]
45015    pub fn parse_ddl_select(&mut self) -> Result<Option<Expression>> {
45016        // Parse a nested SELECT statement
45017        let select = self.parse_select_query()?;
45018
45019        if select.is_none() {
45020            return Ok(None);
45021        }
45022
45023        // Apply set operations (UNION, INTERSECT, EXCEPT)
45024        let with_set_ops = self.parse_set_operations_with_expr(select)?;
45025
45026        // Return the result (query modifiers would be applied by parse_select_query already)
45027        Ok(with_set_ops)
45028    }
45029
45030    /// parse_for_in - BigQuery procedural FOR...IN...DO loop
45031    /// Python: BigQuery._parse_for_in
45032    /// Format: FOR variable IN (query) DO statement(s) END FOR
45033    /// Example: FOR record IN (SELECT * FROM t) DO SELECT record.col
45034    pub fn parse_for_in(&mut self) -> Result<Expression> {
45035        // Parse: variable IN (query)
45036        // This is handled by parse_range which produces an In expression
45037        let this = self
45038            .parse_range()?
45039            .ok_or_else(|| self.parse_error("Expected expression after FOR"))?;
45040
45041        // Match DO keyword
45042        self.match_text_seq(&["DO"]);
45043
45044        // Parse the body statement
45045        let expression = self.parse_statement()?;
45046
45047        Ok(Expression::ForIn(Box::new(ForIn {
45048            this: Box::new(this),
45049            expression: Box::new(expression),
45050        })))
45051    }
45052
45053    /// parse_declare - Parses DECLARE statement
45054    /// Python: _parse_declare
45055    /// Format: DECLARE var1 type [DEFAULT expr], var2 type [DEFAULT expr], ...
45056    pub fn parse_declare(&mut self) -> Result<Option<Expression>> {
45057        // Check for OR REPLACE (Spark/Databricks)
45058        let replace = self.match_text_seq(&["OR", "REPLACE"]);
45059
45060        // Try to parse comma-separated declare items
45061        let mut expressions = Vec::new();
45062
45063        // BigQuery multi-variable DECLARE: DECLARE X, Y, Z INT64 [DEFAULT expr]
45064        // Detect by looking ahead: if we see identifier, comma, identifier pattern
45065        // before a data type keyword, collect all names then parse type once.
45066        let saved = self.current;
45067        let mut multi_names: Vec<Expression> = Vec::new();
45068        if let Some(first_var) = self.parse_id_var()? {
45069            // Check if next is a comma (BigQuery multi-var syntax)
45070            if self.check(TokenType::Comma) && !self.check_identifier("CURSOR") {
45071                // Speculatively collect comma-separated identifiers
45072                multi_names.push(first_var);
45073                while self.match_token(TokenType::Comma) {
45074                    if let Some(next_var) = self.parse_id_var()? {
45075                        multi_names.push(next_var);
45076                    } else {
45077                        break;
45078                    }
45079                }
45080                // Now check if we're at a data type (not comma, not @, not semicolon)
45081                // If so, this is BigQuery multi-var syntax
45082                if multi_names.len() > 1 && !self.is_at_end() && !self.check(TokenType::Semicolon) {
45083                    let data_type = self.parse_data_type()?;
45084                    let kind_str = self.data_type_to_sql(&data_type);
45085                    let default = if self.match_token(TokenType::Default)
45086                        || self.match_token(TokenType::Eq)
45087                    {
45088                        Some(Box::new(self.parse_expression()?))
45089                    } else {
45090                        None
45091                    };
45092                    let first_name = multi_names.remove(0);
45093                    expressions.push(Expression::DeclareItem(Box::new(DeclareItem {
45094                        this: Box::new(first_name),
45095                        kind: Some(kind_str),
45096                        default,
45097                        has_as: false,
45098                        additional_names: multi_names,
45099                    })));
45100                    return Ok(Some(Expression::Declare(Box::new(Declare {
45101                        expressions,
45102                        replace,
45103                    }))));
45104                }
45105            }
45106        }
45107        // Reset and parse normally
45108        self.current = saved;
45109
45110        loop {
45111            if let Some(item) = self.parse_declareitem()? {
45112                expressions.push(item);
45113            } else {
45114                break;
45115            }
45116            if !self.match_token(TokenType::Comma) {
45117                break;
45118            }
45119        }
45120
45121        // If we successfully parsed at least one item, return the Declare
45122        if !expressions.is_empty() {
45123            return Ok(Some(Expression::Declare(Box::new(Declare {
45124                expressions,
45125                replace,
45126            }))));
45127        }
45128
45129        Ok(None)
45130    }
45131
45132    /// parse_declareitem - Parse a DECLARE item (variable declaration)
45133    /// TSQL format: @var AS type [= expr] or @var type [= expr]
45134    /// Also handles: DECLARE name CURSOR FOR SELECT ...
45135    /// Also handles: DECLARE @var TABLE (col_defs)
45136    #[allow(unused_variables, unused_mut)]
45137    pub fn parse_declareitem(&mut self) -> Result<Option<Expression>> {
45138        // Consume optional VAR or VARIABLE keyword (Spark/Databricks)
45139        if self.check_identifier("VAR") || self.check_identifier("VARIABLE") {
45140            self.skip();
45141        }
45142
45143        // Parse the variable name (starts with @ or is a cursor name)
45144        let var = if let Some(v) = self.parse_id_var()? {
45145            v
45146        } else {
45147            return Ok(None);
45148        };
45149
45150        // Check for CURSOR FOR syntax: DECLARE name CURSOR FOR SELECT ...
45151        if self.check_identifier("CURSOR") {
45152            self.skip(); // consume CURSOR
45153                         // Parse optional cursor options before FOR (e.g., SCROLL, INSENSITIVE, etc.)
45154                         // For now just look for FOR
45155            if self.match_token(TokenType::For) {
45156                // Capture the remaining tokens as the cursor query using tokens_to_sql for proper spacing
45157                let start = self.current;
45158                while !self.is_at_end() && !self.check(TokenType::Semicolon) {
45159                    self.skip();
45160                }
45161                let query_str = self.tokens_to_sql_uppercased(start, self.current);
45162                let kind_str = format!("CURSOR FOR {}", query_str);
45163                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45164                    this: Box::new(var),
45165                    kind: Some(kind_str),
45166                    default: None,
45167                    has_as: false,
45168                    additional_names: Vec::new(),
45169                }))));
45170            } else {
45171                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45172                    this: Box::new(var),
45173                    kind: Some("CURSOR".to_string()),
45174                    default: None,
45175                    has_as: false,
45176                    additional_names: Vec::new(),
45177                }))));
45178            }
45179        }
45180
45181        // Parse optional AS keyword
45182        let has_as = self.match_token(TokenType::As);
45183
45184        // Check for TABLE type with column definitions
45185        if self.check(TokenType::Table) {
45186            self.skip(); // consume TABLE
45187            if self.match_token(TokenType::LParen) {
45188                // Parse the TABLE column definitions using tokens_to_sql for proper spacing
45189                let start = self.current;
45190                let mut depth = 1;
45191                while depth > 0 && !self.is_at_end() {
45192                    if self.check(TokenType::LParen) {
45193                        depth += 1;
45194                    }
45195                    if self.check(TokenType::RParen) {
45196                        depth -= 1;
45197                        if depth == 0 {
45198                            break;
45199                        }
45200                    }
45201                    self.skip();
45202                }
45203                let col_defs_str = self.tokens_to_sql_uppercased(start, self.current);
45204                self.expect(TokenType::RParen)?;
45205                let kind_str = format!("TABLE ({})", col_defs_str);
45206                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45207                    this: Box::new(var),
45208                    kind: Some(kind_str),
45209                    default: None,
45210                    has_as,
45211                    additional_names: Vec::new(),
45212                }))));
45213            } else {
45214                return Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45215                    this: Box::new(var),
45216                    kind: Some("TABLE".to_string()),
45217                    default: None,
45218                    has_as,
45219                    additional_names: Vec::new(),
45220                }))));
45221            }
45222        }
45223
45224        // Check if next token is = or DEFAULT (no type, just default value)
45225        // or if at end of statement (no type, no default)
45226        let kind_str = if self.check(TokenType::Eq)
45227            || self.check(TokenType::Default)
45228            || self.is_at_end()
45229            || self.check(TokenType::Semicolon)
45230            || self.check(TokenType::Comma)
45231        {
45232            // No type specified
45233            None
45234        } else {
45235            // Parse the data type
45236            let data_type = self.parse_data_type()?;
45237            Some(self.data_type_to_sql(&data_type))
45238        };
45239
45240        // Parse optional DEFAULT value or = value (TSQL uses =)
45241        let default = if self.match_token(TokenType::Default) || self.match_token(TokenType::Eq) {
45242            Some(Box::new(self.parse_expression()?))
45243        } else {
45244            None
45245        };
45246
45247        Ok(Some(Expression::DeclareItem(Box::new(DeclareItem {
45248            this: Box::new(var),
45249            kind: kind_str,
45250            default,
45251            has_as,
45252            additional_names: Vec::new(),
45253        }))))
45254    }
45255
45256    /// Convert a DataType to its SQL string representation
45257    fn data_type_to_sql(&self, dt: &DataType) -> String {
45258        match dt {
45259            DataType::Boolean => "BOOLEAN".to_string(),
45260            DataType::TinyInt { length } => {
45261                if let Some(n) = length {
45262                    format!("TINYINT({})", n)
45263                } else {
45264                    "TINYINT".to_string()
45265                }
45266            }
45267            DataType::SmallInt { length } => {
45268                if let Some(n) = length {
45269                    format!("SMALLINT({})", n)
45270                } else {
45271                    "SMALLINT".to_string()
45272                }
45273            }
45274            DataType::Int {
45275                length,
45276                integer_spelling,
45277            } => {
45278                if let Some(n) = length {
45279                    if *integer_spelling {
45280                        format!("INTEGER({})", n)
45281                    } else {
45282                        format!("INT({})", n)
45283                    }
45284                } else if *integer_spelling {
45285                    "INTEGER".to_string()
45286                } else {
45287                    "INT".to_string()
45288                }
45289            }
45290            DataType::BigInt { length } => {
45291                if let Some(n) = length {
45292                    format!("BIGINT({})", n)
45293                } else {
45294                    "BIGINT".to_string()
45295                }
45296            }
45297            DataType::Float {
45298                precision, scale, ..
45299            } => match (precision, scale) {
45300                (Some(p), Some(s)) => format!("FLOAT({}, {})", p, s),
45301                (Some(p), None) => format!("FLOAT({})", p),
45302                _ => "FLOAT".to_string(),
45303            },
45304            DataType::Double { precision, scale } => match (precision, scale) {
45305                (Some(p), Some(s)) => format!("DOUBLE({}, {})", p, s),
45306                (Some(p), None) => format!("DOUBLE({})", p),
45307                _ => "DOUBLE".to_string(),
45308            },
45309            DataType::Decimal { precision, scale } => match (precision, scale) {
45310                (Some(p), Some(s)) => format!("DECIMAL({}, {})", p, s),
45311                (Some(p), None) => format!("DECIMAL({})", p),
45312                _ => "DECIMAL".to_string(),
45313            },
45314            DataType::Char { length } => {
45315                if let Some(n) = length {
45316                    format!("CHAR({})", n)
45317                } else {
45318                    "CHAR".to_string()
45319                }
45320            }
45321            DataType::VarChar { length, .. } => {
45322                if let Some(n) = length {
45323                    format!("VARCHAR({})", n)
45324                } else {
45325                    "VARCHAR".to_string()
45326                }
45327            }
45328            DataType::Text => "TEXT".to_string(),
45329            DataType::Date => "DATE".to_string(),
45330            DataType::Time { precision, .. } => {
45331                if let Some(p) = precision {
45332                    format!("TIME({})", p)
45333                } else {
45334                    "TIME".to_string()
45335                }
45336            }
45337            DataType::Timestamp { precision, .. } => {
45338                if let Some(p) = precision {
45339                    format!("TIMESTAMP({})", p)
45340                } else {
45341                    "TIMESTAMP".to_string()
45342                }
45343            }
45344            DataType::Binary { length } => {
45345                if let Some(n) = length {
45346                    format!("BINARY({})", n)
45347                } else {
45348                    "BINARY".to_string()
45349                }
45350            }
45351            DataType::VarBinary { length } => {
45352                if let Some(n) = length {
45353                    format!("VARBINARY({})", n)
45354                } else {
45355                    "VARBINARY".to_string()
45356                }
45357            }
45358            DataType::Blob => "BLOB".to_string(),
45359            DataType::String { length: Some(n) } => format!("STRING({})", n),
45360            DataType::String { length: None } => "STRING".to_string(),
45361            DataType::Json => "JSON".to_string(),
45362            DataType::Uuid => "UUID".to_string(),
45363            DataType::Custom { name } => name.clone(), // Custom types (INT64, FLOAT64, etc.)
45364            _ => format!("{:?}", dt),                  // Fallback for unknown types
45365        }
45366    }
45367
45368    /// parse_decode - Ported from Python _parse_decode
45369    /// Parses Oracle-style DECODE or simple DECODE function
45370    /// If 3+ args: Oracle DECODE(expr, search1, result1, ..., default)
45371    /// If 2 args: character set decode (expr, charset)
45372    #[allow(unused_variables, unused_mut)]
45373    pub fn parse_decode(&mut self) -> Result<Option<Expression>> {
45374        // Parse comma-separated arguments
45375        let mut args: Vec<Expression> = Vec::new();
45376        loop {
45377            match self.parse_expression() {
45378                Ok(expr) => args.push(expr),
45379                Err(_) => break,
45380            }
45381            if !self.match_token(TokenType::Comma) {
45382                break;
45383            }
45384        }
45385
45386        if args.len() < 3 {
45387            // Simple decode with charset
45388            return Ok(Some(Expression::DecodeCase(Box::new(DecodeCase {
45389                expressions: args,
45390            }))));
45391        }
45392
45393        // Oracle DECODE: first arg is the expression being compared
45394        // Remaining args are search/result pairs, with optional default at end
45395        Ok(Some(Expression::DecodeCase(Box::new(DecodeCase {
45396            expressions: args,
45397        }))))
45398    }
45399
45400    /// parse_definer - MySQL DEFINER property
45401    /// Parses: DEFINER = user@host
45402    #[allow(unused_variables, unused_mut)]
45403    pub fn parse_definer(&mut self) -> Result<Option<Expression>> {
45404        // Optionally consume = sign
45405        self.match_token(TokenType::Eq);
45406
45407        // Parse the user part
45408        let user = self.parse_id_var()?;
45409        if user.is_none() {
45410            return Ok(None);
45411        }
45412
45413        // Expect @ symbol
45414        if !self.match_token(TokenType::DAt) {
45415            return Ok(None);
45416        }
45417
45418        // Parse the host part (can be identifier or % wildcard)
45419        let host = if let Some(id) = self.parse_id_var()? {
45420            id
45421        } else if self.match_token(TokenType::Mod) {
45422            // % wildcard for any host
45423            Expression::Identifier(Identifier::new(self.previous().text.clone()))
45424        } else {
45425            return Ok(None);
45426        };
45427
45428        // Combine user@host into a string
45429        let user_str = match &user {
45430            Some(Expression::Identifier(id)) => id.name.clone(),
45431            _ => "".to_string(),
45432        };
45433        let host_str = match &host {
45434            Expression::Identifier(id) => id.name.clone(),
45435            _ => "".to_string(),
45436        };
45437
45438        let definer_str = format!("{}@{}", user_str, host_str);
45439
45440        Ok(Some(Expression::DefinerProperty(Box::new(
45441            DefinerProperty {
45442                this: Box::new(Expression::Literal(Box::new(Literal::String(definer_str)))),
45443            },
45444        ))))
45445    }
45446
45447    /// parse_derived_table_values - Implemented from Python _parse_derived_table_values
45448    #[allow(unused_variables, unused_mut)]
45449    pub fn parse_derived_table_values(&mut self) -> Result<Option<Expression>> {
45450        if self.match_text_seq(&["VALUES"]) {
45451            return Ok(Some(Expression::Values(Box::new(Values {
45452                expressions: Vec::new(),
45453                alias: None,
45454                column_aliases: Vec::new(),
45455            }))));
45456        }
45457        if self.match_text_seq(&["FORMAT", "VALUES"]) {
45458            // Matched: FORMAT VALUES
45459            return Ok(None);
45460        }
45461        Ok(None)
45462    }
45463
45464    /// parse_dict_property - ClickHouse dictionary property
45465    /// Parses: property_name(kind(key1 value1, key2 value2, ...))
45466    /// property_name should be the already matched property keyword (LAYOUT, SOURCE, etc.)
45467    #[allow(unused_variables, unused_mut)]
45468    pub fn parse_dict_property(&mut self, property_name: &str) -> Result<Option<Expression>> {
45469        // Expect opening paren
45470        if !self.match_token(TokenType::LParen) {
45471            return Ok(None);
45472        }
45473
45474        // Parse the kind (e.g., HASHED, FLAT, CLICKHOUSE, CACHE, etc.)
45475        // Accept Var, Identifier, or keyword tokens as the kind name
45476        let kind_str = if self.is_identifier_token() || self.check_keyword() {
45477            self.advance().text.clone()
45478        } else {
45479            String::new()
45480        };
45481        if kind_str.is_empty() {
45482            return Err(self.parse_error("Expected dictionary property kind"));
45483        }
45484
45485        // Parse optional settings in nested parens
45486        let settings = if self.match_token(TokenType::LParen) {
45487            let mut setting_pairs = Vec::new();
45488            loop {
45489                let key = if let Some(k) = self.parse_id_var()? {
45490                    Some(k)
45491                } else if self.is_safe_keyword_as_identifier() || self.check_keyword() {
45492                    let name = self.advance().text.clone();
45493                    Some(Expression::Identifier(Identifier::new(name)))
45494                } else if !self.check(TokenType::RParen) && !self.check(TokenType::Comma) {
45495                    let name = self.advance().text.clone();
45496                    Some(Expression::Identifier(Identifier::new(name)))
45497                } else {
45498                    None
45499                };
45500                // ClickHouse: STRUCTURE (...) contains column defs without commas — consume balanced parens
45501                let is_structure = key.as_ref().map_or(false, |k| {
45502                    matches!(k, Expression::Identifier(id) if id.name.eq_ignore_ascii_case("STRUCTURE"))
45503                });
45504                let value = if is_structure && self.check(TokenType::LParen) {
45505                    let mut raw = String::new();
45506                    let mut depth = 0i32;
45507                    while !self.is_at_end() {
45508                        let tok = self.advance();
45509                        match tok.token_type {
45510                            TokenType::LParen => {
45511                                depth += 1;
45512                                raw.push('(');
45513                            }
45514                            TokenType::RParen => {
45515                                depth -= 1;
45516                                if depth == 0 {
45517                                    raw.push(')');
45518                                    break;
45519                                }
45520                                raw.push(')');
45521                            }
45522                            _ => {
45523                                if !raw.is_empty() && !raw.ends_with('(') {
45524                                    raw.push(' ');
45525                                }
45526                                raw.push_str(&tok.text);
45527                            }
45528                        }
45529                    }
45530                    Some(Expression::Var(Box::new(Var { this: raw })))
45531                } else {
45532                    self.parse_primary_or_var()?
45533                };
45534                if key.is_none() && value.is_none() {
45535                    break;
45536                }
45537                if let (Some(k), Some(v)) = (key, value) {
45538                    // Store as a tuple-like expression
45539                    setting_pairs.push(Expression::Tuple(Box::new(Tuple {
45540                        expressions: vec![k, v],
45541                    })));
45542                }
45543                // ClickHouse dict properties are space-separated, not comma-separated
45544                // e.g. SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() DB 'test'))
45545                // Accept optional comma but don't require it
45546                self.match_token(TokenType::Comma);
45547                // Break if we see RParen (end of settings)
45548                if self.check(TokenType::RParen) {
45549                    break;
45550                }
45551            }
45552            self.expect(TokenType::RParen)?;
45553            if !setting_pairs.is_empty() {
45554                Some(Box::new(Expression::Tuple(Box::new(Tuple {
45555                    expressions: setting_pairs,
45556                }))))
45557            } else {
45558                None
45559            }
45560        } else {
45561            None
45562        };
45563
45564        self.expect(TokenType::RParen)?;
45565
45566        Ok(Some(Expression::DictProperty(Box::new(DictProperty {
45567            this: Box::new(Expression::Identifier(Identifier::new(
45568                property_name.to_string(),
45569            ))),
45570            kind: kind_str,
45571            settings,
45572        }))))
45573    }
45574
45575    /// parse_dict_range - Implemented from Python _parse_dict_range
45576    /// Parses dictionary range specification: (MIN min_val MAX max_val) or (max_val)
45577    pub fn parse_dict_range(&mut self, property_name: &str) -> Result<Option<Expression>> {
45578        // Expect opening paren
45579        self.expect(TokenType::LParen)?;
45580
45581        // Prefer id/var first for dictionary bounds to avoid function-keyword ambiguity
45582        // such as `MIN discount_start_date MAX discount_end_date`.
45583        let parse_bound = |parser: &mut Parser| -> Result<Option<Expression>> {
45584            // Handle negative numbers: -1, -100, etc.
45585            if parser.check(TokenType::Dash)
45586                && parser
45587                    .peek_nth(1)
45588                    .is_some_and(|t| t.token_type == TokenType::Number)
45589            {
45590                parser.advance(); // consume -
45591                let num = parser.advance().text.clone();
45592                return Ok(Some(Expression::Literal(Box::new(Literal::Number(
45593                    format!("-{}", num),
45594                )))));
45595            }
45596            if let Some(id) = parser.parse_id_var()? {
45597                return Ok(Some(id));
45598            }
45599            parser.parse_primary_or_var()
45600        };
45601
45602        let (min_val, max_val) = if self.peek().text.eq_ignore_ascii_case("MIN") {
45603            self.skip(); // consume MIN
45604            let min = parse_bound(self)?;
45605            if self.peek().text.eq_ignore_ascii_case("MAX") {
45606                self.skip(); // consume MAX
45607            }
45608            let max = parse_bound(self)?;
45609            (min, max)
45610        } else {
45611            let max = parse_bound(self)?;
45612            let min = Some(Expression::Literal(Box::new(Literal::Number(
45613                "0".to_string(),
45614            ))));
45615            (min, max)
45616        };
45617
45618        // Match closing paren
45619        self.expect(TokenType::RParen)?;
45620
45621        Ok(Some(Expression::DictRange(Box::new(DictRange {
45622            this: Box::new(Expression::Var(Box::new(Var {
45623                this: property_name.to_string(),
45624            }))),
45625            min: min_val.map(Box::new),
45626            max: max_val.map(Box::new),
45627        }))))
45628    }
45629
45630    /// parse_disjunction - Parses OR expressions
45631    /// Python: _parse_disjunction
45632    /// Delegates to the existing parse_or in the operator precedence chain
45633    pub fn parse_disjunction(&mut self) -> Result<Option<Expression>> {
45634        match self.parse_or() {
45635            Ok(expr) => Ok(Some(expr)),
45636            Err(_) => Ok(None),
45637        }
45638    }
45639
45640    /// parse_distkey - Redshift DISTKEY property for distribution key
45641    /// Parses: DISTKEY(column_name)
45642    #[allow(unused_variables, unused_mut)]
45643    pub fn parse_distkey(&mut self) -> Result<Option<Expression>> {
45644        // Parse wrapped column identifier (in parentheses)
45645        if !self.match_token(TokenType::LParen) {
45646            return Ok(None);
45647        }
45648
45649        let column = self.parse_id_var()?;
45650        if column.is_none() {
45651            return Ok(None);
45652        }
45653
45654        self.match_token(TokenType::RParen);
45655
45656        Ok(Some(Expression::DistKeyProperty(Box::new(
45657            DistKeyProperty {
45658                this: Box::new(column.unwrap()),
45659            },
45660        ))))
45661    }
45662
45663    /// parse_distributed_property - Implemented from Python _parse_distributed_property
45664    #[allow(unused_variables, unused_mut)]
45665    /// parse_distributed_property - Parses DISTRIBUTED BY property
45666    /// Python: parser.py:2462-2481
45667    pub fn parse_distributed_property(&mut self) -> Result<Option<Expression>> {
45668        let mut kind = "HASH".to_string();
45669        let mut expressions = Vec::new();
45670
45671        if self.match_text_seq(&["BY", "HASH"]) {
45672            // Parse column list: (col1, col2, ...)
45673            if let Some(wrapped) = self.parse_wrapped_id_vars()? {
45674                if let Expression::Tuple(t) = wrapped {
45675                    expressions = t.expressions;
45676                }
45677            }
45678        } else if self.match_text_seq(&["BY", "RANDOM"]) {
45679            kind = "RANDOM".to_string();
45680        } else {
45681            return Ok(None);
45682        }
45683
45684        // Parse optional BUCKETS
45685        let buckets = if self.match_text_seq(&["BUCKETS"]) {
45686            if !self.match_text_seq(&["AUTO"]) {
45687                self.parse_number()?
45688            } else {
45689                None
45690            }
45691        } else {
45692            None
45693        };
45694
45695        // Parse optional ORDER BY
45696        let order = self.parse_order()?;
45697
45698        Ok(Some(Expression::DistributedByProperty(Box::new(
45699            DistributedByProperty {
45700                expressions,
45701                kind,
45702                buckets: buckets.map(Box::new),
45703                order: order.map(Box::new),
45704            },
45705        ))))
45706    }
45707
45708    /// Parse DROP COLUMN in ALTER TABLE
45709    /// Note: Main ALTER TABLE DROP COLUMN logic is in parse_alter_table -> AlterTableAction::DropColumn
45710    pub fn parse_drop_column(&mut self) -> Result<Option<Expression>> {
45711        // Optionally match COLUMN keyword
45712        self.match_token(TokenType::Column);
45713
45714        // Parse IF EXISTS
45715        let _if_exists = self.match_keywords(&[TokenType::If, TokenType::Exists]);
45716
45717        // Parse the column identifier
45718        if let Some(column) = self.parse_identifier()? {
45719            // Check for CASCADE
45720            let _cascade = self.match_text_seq(&["CASCADE"]);
45721            // Return the column as an identifier (the caller handles the drop semantics)
45722            Ok(Some(column))
45723        } else {
45724            Ok(None)
45725        }
45726    }
45727
45728    /// Parse DROP PARTITION in ALTER TABLE
45729    /// Note: Main ALTER TABLE DROP PARTITION logic is in parse_alter_table -> AlterTableAction::DropPartition
45730    pub fn parse_drop_partition(&mut self) -> Result<Option<Expression>> {
45731        self.parse_drop_partition_with_exists(false)
45732    }
45733
45734    /// Parse DROP PARTITION with exists flag
45735    pub fn parse_drop_partition_with_exists(&mut self, exists: bool) -> Result<Option<Expression>> {
45736        // Parse one or more partitions
45737        let mut partitions = Vec::new();
45738
45739        loop {
45740            // Parse PARTITION (key = value, ...)
45741            if self.match_token(TokenType::Partition) {
45742                if self.match_token(TokenType::LParen) {
45743                    // Parse partition expressions
45744                    let mut exprs = Vec::new();
45745                    loop {
45746                        let expr = self.parse_expression()?;
45747                        exprs.push(expr);
45748                        if !self.match_token(TokenType::Comma) {
45749                            break;
45750                        }
45751                    }
45752                    self.match_token(TokenType::RParen);
45753                    partitions.push(Expression::Tuple(Box::new(Tuple { expressions: exprs })));
45754                }
45755            } else {
45756                break;
45757            }
45758
45759            if !self.match_token(TokenType::Comma) {
45760                break;
45761            }
45762        }
45763
45764        if partitions.is_empty() {
45765            Ok(None)
45766        } else {
45767            Ok(Some(Expression::DropPartition(Box::new(DropPartition {
45768                expressions: partitions,
45769                exists,
45770            }))))
45771        }
45772    }
45773
45774    /// parse_equality - Parses comparison/equality expressions (= <> < > <= >=)
45775    /// Python: _parse_equality
45776    /// Delegates to the existing parse_comparison in the operator precedence chain
45777    pub fn parse_equality(&mut self) -> Result<Option<Expression>> {
45778        match self.parse_comparison() {
45779            Ok(expr) => Ok(Some(expr)),
45780            Err(_) => Ok(None),
45781        }
45782    }
45783
45784    /// parse_escape - Parses ESCAPE clause for LIKE patterns
45785    /// Python: _parse_escape
45786    /// Returns the escape character/expression if ESCAPE keyword is found
45787    pub fn parse_escape(&mut self) -> Result<Option<Expression>> {
45788        if !self.match_token(TokenType::Escape) {
45789            return Ok(None);
45790        }
45791
45792        // Parse escape character (usually a string like '\')
45793        if let Some(escape_char) = self.parse_string()? {
45794            return Ok(Some(escape_char));
45795        }
45796
45797        // Or parse NULL
45798        if let Some(null_expr) = self.parse_null()? {
45799            return Ok(Some(null_expr));
45800        }
45801
45802        Ok(None)
45803    }
45804
45805    /// parse_exists - Implemented from Python _parse_exists
45806    #[allow(unused_variables, unused_mut)]
45807    pub fn parse_exists(&mut self) -> Result<Option<Expression>> {
45808        if self.match_text_seq(&["IF"]) {
45809            // Matched: IF
45810            return Ok(None);
45811        }
45812        Ok(None)
45813    }
45814
45815    /// parse_exponent - Parses exponent/power expressions
45816    /// Python: _parse_exponent
45817    /// In most dialects, EXPONENT is empty, so this delegates to parse_unary
45818    pub fn parse_exponent(&mut self) -> Result<Option<Expression>> {
45819        match self.parse_unary() {
45820            Ok(expr) => Ok(Some(expr)),
45821            Err(_) => Ok(None),
45822        }
45823    }
45824
45825    /// parse_expressions - Parse comma-separated expressions
45826    /// Returns a Tuple containing all expressions, or None if empty
45827    #[allow(unused_variables, unused_mut)]
45828    pub fn parse_expressions(&mut self) -> Result<Option<Expression>> {
45829        let expressions = self.parse_expression_list()?;
45830        if expressions.is_empty() {
45831            return Ok(None);
45832        }
45833        if expressions.len() == 1 {
45834            return Ok(expressions.into_iter().next());
45835        }
45836        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
45837    }
45838
45839    /// parse_extract - Ported from Python _parse_extract
45840    /// Parses EXTRACT(field FROM expression) function
45841    #[allow(unused_variables, unused_mut)]
45842    pub fn parse_extract(&mut self) -> Result<Option<Expression>> {
45843        // Parse the field (YEAR, MONTH, DAY, HOUR, etc.)
45844        let field_name = if self.check(TokenType::Identifier) || self.check(TokenType::Var) {
45845            let token = self.advance();
45846            token.text.to_ascii_uppercase()
45847        } else {
45848            return Ok(None);
45849        };
45850
45851        // Convert field name to DateTimeField
45852        let field = match field_name.as_str() {
45853            "YEAR" => DateTimeField::Year,
45854            "MONTH" => DateTimeField::Month,
45855            "DAY" => DateTimeField::Day,
45856            "HOUR" => DateTimeField::Hour,
45857            "MINUTE" => DateTimeField::Minute,
45858            "SECOND" => DateTimeField::Second,
45859            "MILLISECOND" | "MILLISECONDS" | "MS" => DateTimeField::Millisecond,
45860            "MICROSECOND" | "MICROSECONDS" | "US" => DateTimeField::Microsecond,
45861            "DOW" | "DAYOFWEEK" => DateTimeField::DayOfWeek,
45862            "DOY" | "DAYOFYEAR" => DateTimeField::DayOfYear,
45863            "WEEK" => DateTimeField::Week,
45864            "QUARTER" => DateTimeField::Quarter,
45865            "EPOCH" => DateTimeField::Epoch,
45866            "TIMEZONE" => DateTimeField::Timezone,
45867            "TIMEZONE_HOUR" => DateTimeField::TimezoneHour,
45868            "TIMEZONE_MINUTE" => DateTimeField::TimezoneMinute,
45869            "DATE" => DateTimeField::Date,
45870            "TIME" => DateTimeField::Time,
45871            other => DateTimeField::Custom(other.to_string()),
45872        };
45873
45874        // Expect FROM or comma
45875        if !self.match_token(TokenType::From) && !self.match_token(TokenType::Comma) {
45876            return Err(self.parse_error("Expected FROM or comma after EXTRACT field"));
45877        }
45878
45879        // Parse the expression to extract from
45880        let expression = self.parse_bitwise()?;
45881        let this = match expression {
45882            Some(expr) => self.try_clickhouse_func_arg_alias(expr),
45883            None => return Err(self.parse_error("Expected expression after FROM in EXTRACT")),
45884        };
45885
45886        Ok(Some(Expression::Extract(Box::new(ExtractFunc {
45887            this,
45888            field,
45889        }))))
45890    }
45891
45892    /// parse_factor - Parses multiplication/division expressions (* / % operators)
45893    /// Python: _parse_factor
45894    /// Delegates to the existing parse_multiplication in the operator precedence chain
45895    pub fn parse_factor(&mut self) -> Result<Option<Expression>> {
45896        // Delegate to the existing multiplication parsing
45897        match self.parse_multiplication() {
45898            Ok(expr) => Ok(Some(expr)),
45899            Err(_) => Ok(None),
45900        }
45901    }
45902
45903    /// parse_fallback - Implemented from Python _parse_fallback
45904    #[allow(unused_variables, unused_mut)]
45905    pub fn parse_fallback(&mut self) -> Result<Option<Expression>> {
45906        if self.match_text_seq(&["PROTECTION"]) {
45907            return Ok(Some(Expression::FallbackProperty(Box::new(
45908                FallbackProperty {
45909                    no: None,
45910                    protection: None,
45911                },
45912            ))));
45913        }
45914        Ok(None)
45915    }
45916
45917    /// parse_field - Parse a field (column name, literal, or expression)
45918    /// Python: field = self._parse_primary() or self._parse_function() or self._parse_id_var()
45919    pub fn parse_field(&mut self) -> Result<Option<Expression>> {
45920        // Try parsing literals first
45921        if let Some(expr) = self.parse_string()? {
45922            return Ok(Some(expr));
45923        }
45924        if let Some(expr) = self.parse_number()? {
45925            return Ok(Some(expr));
45926        }
45927        if let Some(expr) = self.parse_boolean()? {
45928            return Ok(Some(expr));
45929        }
45930        if let Some(expr) = self.parse_null()? {
45931            return Ok(Some(expr));
45932        }
45933        if let Some(expr) = self.parse_star()? {
45934            return Ok(Some(expr));
45935        }
45936        // Try parsing identifier
45937        if let Some(expr) = self.parse_identifier()? {
45938            return Ok(Some(expr));
45939        }
45940        // Try parsing a variable/identifier
45941        if let Some(expr) = self.parse_var()? {
45942            return Ok(Some(expr));
45943        }
45944        // Allow keywords as identifiers in field context (e.g., "schema" as a field name)
45945        if self.check_keyword() {
45946            let token = self.advance();
45947            return Ok(Some(Expression::Identifier(Identifier {
45948                name: token.text,
45949                quoted: false,
45950                trailing_comments: Vec::new(),
45951                span: None,
45952            })));
45953        }
45954        Ok(None)
45955    }
45956
45957    /// parse_field_def - Ported from Python _parse_field_def
45958    /// Parses a field definition (column name + type + optional constraints)
45959    #[allow(unused_variables, unused_mut)]
45960    pub fn parse_field_def(&mut self) -> Result<Option<Expression>> {
45961        // First parse the field name (identifier)
45962        let field = self.parse_field()?;
45963
45964        if field.is_none() {
45965            return Ok(None);
45966        }
45967
45968        // Parse the column definition with the field as the name
45969        self.parse_column_def_with_field(field)
45970    }
45971
45972    /// Helper to parse a column definition with a pre-parsed field name
45973    fn parse_column_def_with_field(
45974        &mut self,
45975        field: Option<Expression>,
45976    ) -> Result<Option<Expression>> {
45977        if field.is_none() {
45978            return Ok(None);
45979        }
45980
45981        let this = field.unwrap();
45982
45983        // Get the identifier from the expression and preserve quoted-identifier state.
45984        let name_ident = match &this {
45985            Expression::Column(col) => col.name.clone(),
45986            Expression::Identifier(id) => id.clone(),
45987            Expression::Var(v) => Identifier::new(v.this.clone()),
45988            _ => return Ok(None),
45989        };
45990
45991        // Parse the data type using parse_data_type_optional (which handles unknown types gracefully)
45992        let data_type = match self.parse_data_type_optional()? {
45993            Some(dt) => dt,
45994            None => DataType::Unknown,
45995        };
45996
45997        // Create ColumnDef with default values
45998        let mut col_def = ColumnDef::new(name_ident.name.clone(), data_type);
45999        col_def.name = name_ident;
46000
46001        // Check for FOR ORDINALITY (JSON table columns)
46002        if self.match_text_seq(&["FOR", "ORDINALITY"]) {
46003            return Ok(Some(Expression::ColumnDef(Box::new(col_def))));
46004        }
46005
46006        // Parse constraints and extract specific constraint values
46007        loop {
46008            if let Some(constraint) = self.parse_column_constraint()? {
46009                // Check specific constraint types
46010                match &constraint {
46011                    Expression::NotNullColumnConstraint(_) => {
46012                        col_def.nullable = Some(false);
46013                        col_def.constraints.push(ColumnConstraint::NotNull);
46014                    }
46015                    Expression::PrimaryKeyColumnConstraint(_) => {
46016                        col_def.primary_key = true;
46017                        col_def.constraints.push(ColumnConstraint::PrimaryKey);
46018                    }
46019                    Expression::UniqueColumnConstraint(_) => {
46020                        col_def.unique = true;
46021                        col_def.constraints.push(ColumnConstraint::Unique);
46022                    }
46023                    Expression::DefaultColumnConstraint(dc) => {
46024                        col_def.default = Some((*dc.this).clone());
46025                        col_def
46026                            .constraints
46027                            .push(ColumnConstraint::Default((*dc.this).clone()));
46028                    }
46029                    Expression::AutoIncrementColumnConstraint(_) => {
46030                        col_def.auto_increment = true;
46031                    }
46032                    Expression::CommentColumnConstraint(_) => {
46033                        // Comment is a unit struct, we'd need the actual comment text
46034                    }
46035                    Expression::CheckColumnConstraint(cc) => {
46036                        col_def
46037                            .constraints
46038                            .push(ColumnConstraint::Check((*cc.this).clone()));
46039                    }
46040                    Expression::PathColumnConstraint(pc) => {
46041                        col_def
46042                            .constraints
46043                            .push(ColumnConstraint::Path((*pc.this).clone()));
46044                        col_def.constraint_order.push(ConstraintType::Path);
46045                    }
46046                    _ => {}
46047                }
46048            } else if matches!(
46049                self.config.dialect,
46050                Some(crate::dialects::DialectType::ClickHouse)
46051            ) && self.match_identifier("ALIAS")
46052            {
46053                // ClickHouse: ALIAS expr
46054                let expr = self.parse_or()?;
46055                col_def.alias_expr = Some(Box::new(expr));
46056            } else if matches!(
46057                self.config.dialect,
46058                Some(crate::dialects::DialectType::ClickHouse)
46059            ) && self.check(TokenType::Materialized)
46060                && !self.check_next(TokenType::View)
46061            {
46062                // ClickHouse: MATERIALIZED expr
46063                self.skip(); // consume MATERIALIZED
46064                let expr = self.parse_or()?;
46065                col_def.materialized_expr = Some(Box::new(expr));
46066            } else if matches!(
46067                self.config.dialect,
46068                Some(crate::dialects::DialectType::ClickHouse)
46069            ) && self.match_identifier("EPHEMERAL")
46070            {
46071                // ClickHouse: EPHEMERAL [expr]
46072                if !self.check(TokenType::Comma)
46073                    && !self.check(TokenType::RParen)
46074                    && !self.is_at_end()
46075                    && !self.check_identifier("CODEC")
46076                    && !self.check_identifier("TTL")
46077                    && !self.check(TokenType::Comment)
46078                {
46079                    let expr = self.parse_bitwise()?.unwrap_or(Expression::Null(Null));
46080                    col_def.ephemeral = Some(Some(Box::new(expr)));
46081                } else {
46082                    col_def.ephemeral = Some(None);
46083                }
46084            } else if matches!(
46085                self.config.dialect,
46086                Some(crate::dialects::DialectType::ClickHouse)
46087            ) && self.check_identifier("CODEC")
46088            {
46089                // ClickHouse: CODEC(LZ4HC(9), ZSTD, DELTA)
46090                self.skip(); // consume CODEC
46091                self.expect(TokenType::LParen)?;
46092                let start = self.current;
46093                let mut depth = 1;
46094                while !self.is_at_end() && depth > 0 {
46095                    if self.check(TokenType::LParen) {
46096                        depth += 1;
46097                    }
46098                    if self.check(TokenType::RParen) {
46099                        depth -= 1;
46100                        if depth == 0 {
46101                            break;
46102                        }
46103                    }
46104                    self.skip();
46105                }
46106                let codec_text = self.tokens_to_sql(start, self.current);
46107                self.expect(TokenType::RParen)?;
46108                col_def.codec = Some(codec_text);
46109            } else if matches!(
46110                self.config.dialect,
46111                Some(crate::dialects::DialectType::ClickHouse)
46112            ) && self.match_identifier("TTL")
46113            {
46114                // ClickHouse: TTL expr
46115                let expr = self.parse_expression()?;
46116                col_def.ttl_expr = Some(Box::new(expr));
46117            } else {
46118                break;
46119            }
46120        }
46121
46122        Ok(Some(Expression::ColumnDef(Box::new(col_def))))
46123    }
46124
46125    /// parse_foreign_key - Implemented from Python _parse_foreign_key
46126    /// Calls: parse_key_constraint_options, parse_wrapped_id_vars, parse_references
46127    #[allow(unused_variables, unused_mut)]
46128    pub fn parse_foreign_key(&mut self) -> Result<Option<Expression>> {
46129        if self.match_text_seq(&["NO", "ACTION"]) {
46130            return Ok(Some(Expression::ForeignKey(Box::new(ForeignKey {
46131                expressions: Vec::new(),
46132                reference: None,
46133                delete: None,
46134                update: None,
46135                options: Vec::new(),
46136            }))));
46137        }
46138        Ok(None)
46139    }
46140
46141    /// parse_format_json - Implemented from Python _parse_format_json
46142    #[allow(unused_variables, unused_mut)]
46143    pub fn parse_format_json(&mut self) -> Result<Option<Expression>> {
46144        if self.match_text_seq(&["FORMAT", "JSON"]) {
46145            // Matched: FORMAT JSON
46146            return Ok(None);
46147        }
46148        Ok(None)
46149    }
46150
46151    /// parse_format_name - Snowflake FILE_FORMAT = format_name property
46152    /// Parses: format_name (string or identifier)
46153    #[allow(unused_variables, unused_mut)]
46154    pub fn parse_format_name(&mut self) -> Result<Option<Expression>> {
46155        // Try to parse a string first, then fall back to table parts
46156        let value = if let Some(s) = self.parse_string()? {
46157            s
46158        } else if let Some(tp) = self.parse_table_parts()? {
46159            tp
46160        } else {
46161            return Ok(None);
46162        };
46163
46164        Ok(Some(Expression::Property(Box::new(Property {
46165            this: Box::new(Expression::Identifier(Identifier::new(
46166                "FORMAT_NAME".to_string(),
46167            ))),
46168            value: Some(Box::new(value)),
46169        }))))
46170    }
46171
46172    /// parse_freespace - Teradata FREESPACE property
46173    /// Parses: FREESPACE = number [PERCENT]
46174    #[allow(unused_variables, unused_mut)]
46175    pub fn parse_freespace(&mut self) -> Result<Option<Expression>> {
46176        // Optionally consume = sign
46177        self.match_token(TokenType::Eq);
46178
46179        // Parse the number value
46180        let this = self.parse_number()?;
46181        if this.is_none() {
46182            return Ok(None);
46183        }
46184
46185        // Check for PERCENT keyword
46186        let percent = if self.match_token(TokenType::Percent) {
46187            Some(Box::new(Expression::Boolean(BooleanLiteral {
46188                value: true,
46189            })))
46190        } else {
46191            None
46192        };
46193
46194        Ok(Some(Expression::FreespaceProperty(Box::new(
46195            FreespaceProperty {
46196                this: Box::new(this.unwrap()),
46197                percent,
46198            },
46199        ))))
46200    }
46201
46202    /// parse_function - Ported from Python _parse_function
46203    /// Parses function calls like func_name(args) or {fn func_name(args)} (ODBC syntax)
46204    pub fn parse_function(&mut self) -> Result<Option<Expression>> {
46205        // Check for ODBC escape syntax: {fn function_call}
46206        let fn_syntax = if self.check(TokenType::LBrace) {
46207            if let Some(next) = self.tokens.get(self.current + 1) {
46208                if next.text.eq_ignore_ascii_case("FN") {
46209                    self.skip(); // consume {
46210                    self.skip(); // consume FN
46211                    true
46212                } else {
46213                    false
46214                }
46215            } else {
46216                false
46217            }
46218        } else {
46219            false
46220        };
46221
46222        let func = self.parse_function_call()?;
46223
46224        if fn_syntax {
46225            self.match_token(TokenType::RBrace);
46226        }
46227
46228        Ok(func)
46229    }
46230
46231    /// parse_function_args - Ported from Python _parse_function_args
46232    /// Parses the arguments inside a function call, handling aliases and key-value pairs
46233    pub fn parse_function_args_list(&mut self) -> Result<Vec<Expression>> {
46234        let mut args = Vec::new();
46235
46236        if self.check(TokenType::RParen) {
46237            return Ok(args);
46238        }
46239
46240        loop {
46241            // Try to parse expression with optional alias
46242            if let Some(expr) = self.parse_assignment()? {
46243                // Handle explicit AS alias inside function args (e.g. `tuple(1 AS "a", 2 AS "b")`)
46244                if self.match_token(TokenType::As) {
46245                    let alias_token = self.advance();
46246                    let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
46247                        // Preserve quoted identifiers
46248                        let raw = alias_token.text.clone();
46249                        let mut ident = Identifier::new(raw);
46250                        ident.quoted = true;
46251                        ident
46252                    } else {
46253                        Identifier::new(alias_token.text.clone())
46254                    };
46255                    args.push(Expression::Alias(Box::new(crate::expressions::Alias {
46256                        this: expr,
46257                        alias: alias_name,
46258                        column_aliases: Vec::new(),
46259                        pre_alias_comments: Vec::new(),
46260                        trailing_comments: Vec::new(),
46261                        inferred_type: None,
46262                    })));
46263                } else {
46264                    args.push(expr);
46265                }
46266            }
46267
46268            if !self.match_token(TokenType::Comma) {
46269                break;
46270            }
46271        }
46272
46273        Ok(args)
46274    }
46275
46276    /// parse_function_call - Ported from Python _parse_function_call
46277    /// Parses a function call expression like func_name(arg1, arg2, ...)
46278    pub fn parse_function_call(&mut self) -> Result<Option<Expression>> {
46279        if self.is_at_end() {
46280            return Ok(None);
46281        }
46282
46283        let token = self.peek().clone();
46284        let token_type = token.token_type.clone();
46285        let name = token.text.clone();
46286        let _upper_name = name.to_ascii_uppercase();
46287
46288        // Check for no-paren functions like CURRENT_DATE, CURRENT_TIMESTAMP
46289        if self.is_no_paren_function() {
46290            // Check if next token is NOT a paren (so it's used without parens)
46291            if !self.check_next(TokenType::LParen) {
46292                self.skip();
46293                return Ok(Some(Expression::Function(Box::new(Function {
46294                    name, // Preserve original case; generator handles normalization
46295                    args: Vec::new(),
46296                    distinct: false,
46297                    trailing_comments: Vec::new(),
46298                    use_bracket_syntax: false,
46299                    no_parens: true,
46300                    quoted: false,
46301                    span: None,
46302                    inferred_type: None,
46303                }))));
46304            }
46305        }
46306
46307        // Must be followed by left paren
46308        if !self.check_next(TokenType::LParen) {
46309            return Ok(None);
46310        }
46311
46312        // Token must be a valid function name token
46313        let is_valid_func_token = matches!(
46314            token_type,
46315            TokenType::Identifier
46316                | TokenType::Var
46317                | TokenType::If
46318                | TokenType::Left
46319                | TokenType::Right
46320                | TokenType::Insert
46321                | TokenType::Replace
46322                | TokenType::Row
46323                | TokenType::Index
46324        );
46325        if !is_valid_func_token {
46326            return Ok(None);
46327        }
46328
46329        self.skip(); // consume function name
46330        self.skip(); // consume (
46331
46332        // Check for DISTINCT keyword
46333        let distinct = self.match_token(TokenType::Distinct);
46334
46335        // Parse arguments
46336        let args = self.parse_function_args_list()?;
46337
46338        self.match_token(TokenType::RParen);
46339
46340        // Handle window specifications
46341        let func_expr = Expression::Function(Box::new(Function {
46342            name, // Preserve original case; generator handles normalization
46343            args,
46344            distinct,
46345            trailing_comments: Vec::new(),
46346            use_bracket_syntax: false,
46347            no_parens: false,
46348            quoted: false,
46349            span: None,
46350            inferred_type: None,
46351        }));
46352
46353        // Check for OVER clause (window function)
46354        if self.match_token(TokenType::Over) {
46355            // Parse window spec - create a simple WindowSpec
46356            if self.match_token(TokenType::LParen) {
46357                // Use parse_window_spec_inner to handle DISTRIBUTE BY/SORT BY (Hive)
46358                let spec = self.parse_window_spec_inner()?;
46359                self.expect(TokenType::RParen)?;
46360
46361                if let Some(spec_expr) = spec {
46362                    return Ok(Some(spec_expr));
46363                }
46364            }
46365        }
46366
46367        Ok(Some(func_expr))
46368    }
46369
46370    /// parse_function_parameter - Ported from Python _parse_function_parameter
46371    /// Parses a function parameter in CREATE FUNCTION (name type [DEFAULT expr])
46372    pub fn parse_function_parameter(&mut self) -> Result<Option<Expression>> {
46373        // Parse optional parameter mode (IN, OUT, INOUT)
46374        let _mode = if self.match_texts(&["IN"]) {
46375            if self.match_texts(&["OUT"]) {
46376                Some(ParameterMode::InOut)
46377            } else {
46378                Some(ParameterMode::In)
46379            }
46380        } else if self.match_texts(&["OUT"]) {
46381            Some(ParameterMode::Out)
46382        } else if self.match_texts(&["INOUT"]) {
46383            Some(ParameterMode::InOut)
46384        } else {
46385            None
46386        };
46387
46388        // Parse parameter name (optional in some dialects)
46389        let name_expr = self.parse_id_var()?;
46390        let name = name_expr.and_then(|n| match n {
46391            Expression::Identifier(id) => Some(id),
46392            _ => None,
46393        });
46394
46395        // Parse data type - returns Result<DataType>, not Result<Option<DataType>>
46396        // We need to handle the case where we can't parse a data type
46397        let data_type_result = self.parse_data_type();
46398        let _data_type = match data_type_result {
46399            Ok(dt) => dt,
46400            Err(_) => return Ok(None),
46401        };
46402
46403        // Parse optional DEFAULT value
46404        let _default = if self.match_token(TokenType::Default) || self.match_texts(&["="]) {
46405            self.parse_disjunction()?
46406        } else {
46407            None
46408        };
46409
46410        // Return the name as a Column expression
46411        Ok(Some(Expression::boxed_column(Column {
46412            name: Identifier {
46413                name: name.map(|n| n.name).unwrap_or_default(),
46414                quoted: false,
46415                trailing_comments: Vec::new(),
46416                span: None,
46417            },
46418            table: None,
46419            join_mark: false,
46420            trailing_comments: Vec::new(),
46421            span: None,
46422            inferred_type: None,
46423        })))
46424    }
46425
46426    /// parse_gap_fill - Ported from Python _parse_gap_fill
46427    #[allow(unused_variables, unused_mut)]
46428    /// parse_gap_fill - Parses GAP_FILL function for time series
46429    /// Example: GAP_FILL(TABLE t, ts_column, bucket_width, partitioning_columns, value_columns)
46430    pub fn parse_gap_fill(&mut self) -> Result<Option<Expression>> {
46431        // Optional TABLE keyword
46432        self.match_token(TokenType::Table);
46433
46434        // Parse the table reference
46435        let this = self.parse_table()?;
46436        if this.is_none() {
46437            return Ok(None);
46438        }
46439
46440        // Parse comma-separated arguments
46441        self.match_token(TokenType::Comma);
46442        let mut args = self.parse_expression_list()?;
46443
46444        // Extract arguments by position
46445        let ts_column = args.get(0).cloned().map(Box::new);
46446        let bucket_width = args.get(1).cloned().map(Box::new);
46447        let partitioning_columns = args.get(2).cloned().map(Box::new);
46448        let value_columns = args.get(3).cloned().map(Box::new);
46449
46450        Ok(Some(Expression::GapFill(Box::new(GapFill {
46451            this: Box::new(this.unwrap()),
46452            ts_column,
46453            bucket_width,
46454            partitioning_columns,
46455            value_columns,
46456            origin: None,
46457            ignore_nulls: None,
46458        }))))
46459    }
46460
46461    /// parse_semantic_view - Parse Snowflake SEMANTIC_VIEW function
46462    /// Example: SEMANTIC_VIEW(foo METRICS a.b, a.c DIMENSIONS a.b, a.c WHERE a.b > '1995-01-01')
46463    pub fn parse_semantic_view(&mut self) -> Result<Expression> {
46464        // Parse the table/view reference as a primary expression (identifier or qualified name)
46465        let this = self.parse_primary()?;
46466
46467        let mut metrics = None;
46468        let mut dimensions = None;
46469        let mut facts = None;
46470        let mut where_clause = None;
46471
46472        // Parse optional clauses: METRICS, DIMENSIONS, FACTS, WHERE
46473        while !self.check(TokenType::RParen) && !self.is_at_end() {
46474            if self.match_identifier("METRICS") {
46475                // Parse comma-separated expressions until next keyword or )
46476                let exprs = self.parse_semantic_view_list()?;
46477                metrics = Some(Box::new(Expression::Tuple(Box::new(Tuple {
46478                    expressions: exprs,
46479                }))));
46480            } else if self.match_identifier("DIMENSIONS") {
46481                let exprs = self.parse_semantic_view_list()?;
46482                dimensions = Some(Box::new(Expression::Tuple(Box::new(Tuple {
46483                    expressions: exprs,
46484                }))));
46485            } else if self.match_identifier("FACTS") {
46486                let exprs = self.parse_semantic_view_list()?;
46487                facts = Some(Box::new(Expression::Tuple(Box::new(Tuple {
46488                    expressions: exprs,
46489                }))));
46490            } else if self.match_token(TokenType::Where) {
46491                // Parse the WHERE expression
46492                where_clause = Some(Box::new(self.parse_expression()?));
46493                // WHERE is the last clause, break after parsing it
46494                break;
46495            } else {
46496                // Unknown token
46497                break;
46498            }
46499        }
46500
46501        Ok(Expression::SemanticView(Box::new(SemanticView {
46502            this: Box::new(this),
46503            metrics,
46504            dimensions,
46505            facts,
46506            where_: where_clause,
46507        })))
46508    }
46509
46510    /// Helper to parse comma-separated expression list for SEMANTIC_VIEW clauses
46511    /// Stops at METRICS, DIMENSIONS, FACTS, WHERE, or )
46512    /// Each element can have an optional AS alias: expr AS name
46513    fn parse_semantic_view_list(&mut self) -> Result<Vec<Expression>> {
46514        let first = self.parse_semantic_view_element()?;
46515        let mut exprs = vec![first];
46516        while self.match_token(TokenType::Comma) {
46517            // Check if next token is a keyword that starts a new clause
46518            if self.check_identifier("METRICS")
46519                || self.check_identifier("DIMENSIONS")
46520                || self.check_identifier("FACTS")
46521                || self.check(TokenType::Where)
46522                || self.check(TokenType::RParen)
46523            {
46524                break;
46525            }
46526            exprs.push(self.parse_semantic_view_element()?);
46527        }
46528        Ok(exprs)
46529    }
46530
46531    /// Parse a single SEMANTIC_VIEW element: expression [AS alias]
46532    fn parse_semantic_view_element(&mut self) -> Result<Expression> {
46533        let expr = self
46534            .parse_disjunction()?
46535            .ok_or_else(|| self.parse_error("Expected expression in SEMANTIC_VIEW clause"))?;
46536        // Check for optional explicit AS alias
46537        if self.match_token(TokenType::As) {
46538            let alias = self.expect_identifier_or_keyword_with_quoted()?;
46539            Ok(Expression::Alias(Box::new(crate::expressions::Alias {
46540                this: expr,
46541                alias,
46542                column_aliases: Vec::new(),
46543                pre_alias_comments: Vec::new(),
46544                trailing_comments: Vec::new(),
46545                inferred_type: None,
46546            })))
46547        } else {
46548            Ok(expr)
46549        }
46550    }
46551
46552    /// parse_grant_principal - Implemented from Python _parse_grant_principal
46553    /// Calls: parse_id_var
46554    #[allow(unused_variables, unused_mut)]
46555    pub fn parse_grant_principal(&mut self) -> Result<Option<Expression>> {
46556        if self.match_texts(&["ROLE", "GROUP"]) {
46557            // Matched one of: ROLE, GROUP
46558            return Ok(None);
46559        }
46560        Ok(None)
46561    }
46562
46563    /// parse_grant_privilege - Parse a single privilege in GRANT/REVOKE
46564    /// Parses: SELECT, INSERT, UPDATE(col1, col2), DELETE, etc.
46565    #[allow(unused_variables, unused_mut)]
46566    pub fn parse_grant_privilege(&mut self) -> Result<Option<Expression>> {
46567        // Collect privilege keywords (SELECT, INSERT, UPDATE, DELETE, ALL PRIVILEGES, etc.)
46568        let mut privilege_parts = Vec::new();
46569
46570        // Keep consuming keywords until we hit a follow token
46571        // Follow tokens are: comma, ON, left paren
46572        while !self.is_at_end() {
46573            // Check if we've hit a follow token
46574            if self.check(TokenType::Comma)
46575                || self.check(TokenType::On)
46576                || self.check(TokenType::LParen)
46577            {
46578                break;
46579            }
46580
46581            // Get the current token text
46582            let text = self.peek().text.to_ascii_uppercase();
46583            privilege_parts.push(text);
46584            self.skip();
46585        }
46586
46587        if privilege_parts.is_empty() {
46588            return Ok(None);
46589        }
46590
46591        let privilege_str = privilege_parts.join(" ");
46592
46593        // Check for column list in parentheses (e.g., UPDATE(col1, col2))
46594        let expressions = if self.match_token(TokenType::LParen) {
46595            let mut columns = Vec::new();
46596            loop {
46597                if let Some(col) = self.parse_column()? {
46598                    columns.push(col);
46599                } else {
46600                    break;
46601                }
46602                if !self.match_token(TokenType::Comma) {
46603                    break;
46604                }
46605            }
46606            self.match_token(TokenType::RParen);
46607            columns
46608        } else {
46609            Vec::new()
46610        };
46611
46612        Ok(Some(Expression::GrantPrivilege(Box::new(GrantPrivilege {
46613            this: Box::new(Expression::Identifier(Identifier::new(privilege_str))),
46614            expressions,
46615        }))))
46616    }
46617
46618    /// parse_grant_revoke_common - Parses common parts of GRANT/REVOKE statements
46619    /// Python: _parse_grant_revoke_common
46620    /// Returns a Tuple containing (privileges, kind, securable)
46621    pub fn parse_grant_revoke_common(&mut self) -> Result<Option<Expression>> {
46622        // Parse privileges (CSV of grant privileges)
46623        let mut privileges = Vec::new();
46624        loop {
46625            if let Some(priv_expr) = self.parse_grant_privilege()? {
46626                privileges.push(priv_expr);
46627            }
46628            if !self.match_token(TokenType::Comma) {
46629                break;
46630            }
46631        }
46632
46633        // Match ON keyword
46634        self.match_token(TokenType::On);
46635
46636        // Parse kind (TABLE, VIEW, SCHEMA, DATABASE, etc.)
46637        let kind = if self.match_texts(&[
46638            "TABLE",
46639            "VIEW",
46640            "SCHEMA",
46641            "DATABASE",
46642            "SEQUENCE",
46643            "FUNCTION",
46644            "PROCEDURE",
46645            "INDEX",
46646            "TYPE",
46647            "TABLESPACE",
46648            "ROLE",
46649            "USER",
46650        ]) {
46651            let kind_text = self.previous().text.to_ascii_uppercase();
46652            Some(Expression::Var(Box::new(Var { this: kind_text })))
46653        } else {
46654            None
46655        };
46656
46657        // Try to parse securable (table parts)
46658        let securable = self.parse_table_parts()?;
46659
46660        // Return as Tuple with three elements: privileges_list, kind, securable
46661        let privileges_expr = Expression::Tuple(Box::new(Tuple {
46662            expressions: privileges,
46663        }));
46664
46665        let mut result_exprs = vec![privileges_expr];
46666
46667        if let Some(k) = kind {
46668            result_exprs.push(k);
46669        } else {
46670            result_exprs.push(Expression::Null(Null));
46671        }
46672
46673        if let Some(s) = securable {
46674            result_exprs.push(s);
46675        } else {
46676            result_exprs.push(Expression::Null(Null));
46677        }
46678
46679        Ok(Some(Expression::Tuple(Box::new(Tuple {
46680            expressions: result_exprs,
46681        }))))
46682    }
46683
46684    /// parse_group - Parse GROUP BY clause
46685    /// Python: if not self._match(TokenType.GROUP_BY): return None; expressions = self._parse_csv(self._parse_disjunction)
46686    pub fn parse_group(&mut self) -> Result<Option<Expression>> {
46687        // Check for GROUP BY token (which should be parsed as Group + By tokens)
46688        if !self.match_token(TokenType::Group) {
46689            return Ok(None);
46690        }
46691        // Consume BY if present
46692        self.match_token(TokenType::By);
46693
46694        // Check for optional ALL/DISTINCT
46695        // Some(true) = ALL, Some(false) = DISTINCT, None = no modifier
46696        let all = if self.match_token(TokenType::All) {
46697            Some(true)
46698        } else if self.match_token(TokenType::Distinct) {
46699            Some(false)
46700        } else {
46701            None
46702        };
46703
46704        // Parse comma-separated expressions
46705        let mut expressions = Vec::new();
46706        loop {
46707            match self.parse_expression() {
46708                Ok(expr) => expressions.push(expr),
46709                Err(_) => break,
46710            }
46711            if !self.match_token(TokenType::Comma) {
46712                break;
46713            }
46714        }
46715
46716        // Handle TOTALS (ClickHouse)
46717        let totals = if self.match_text_seq(&["WITH", "TOTALS"]) {
46718            Some(Box::new(Expression::Boolean(BooleanLiteral {
46719                value: true,
46720            })))
46721        } else if self.match_text_seq(&["TOTALS"]) {
46722            Some(Box::new(Expression::Boolean(BooleanLiteral {
46723                value: true,
46724            })))
46725        } else {
46726            None
46727        };
46728
46729        Ok(Some(Expression::Group(Box::new(Group {
46730            expressions,
46731            grouping_sets: None,
46732            cube: None,
46733            rollup: None,
46734            totals,
46735            all,
46736        }))))
46737    }
46738
46739    /// parse_group_concat - Ported from Python _parse_group_concat
46740    #[allow(unused_variables, unused_mut)]
46741    /// parse_group_concat - Parses MySQL GROUP_CONCAT function
46742    /// Example: GROUP_CONCAT(DISTINCT col ORDER BY col SEPARATOR ',')
46743    pub fn parse_group_concat(&mut self) -> Result<Option<Expression>> {
46744        // Check for DISTINCT
46745        let distinct = self.match_token(TokenType::Distinct);
46746
46747        // Parse expression(s)
46748        let expr = self.parse_expression()?;
46749
46750        // Parse optional ORDER BY
46751        let order_by = if self.match_keywords(&[TokenType::Order, TokenType::By]) {
46752            let mut orderings = Vec::new();
46753            loop {
46754                let order_expr = self.parse_expression()?;
46755                let desc = if self.match_token(TokenType::Desc) {
46756                    true
46757                } else {
46758                    self.match_token(TokenType::Asc);
46759                    false
46760                };
46761                let nulls_first = if self.match_keywords(&[TokenType::Nulls, TokenType::First]) {
46762                    Some(true)
46763                } else if self.match_keywords(&[TokenType::Nulls, TokenType::Last]) {
46764                    Some(false)
46765                } else {
46766                    None
46767                };
46768                orderings.push(Ordered {
46769                    this: order_expr,
46770                    desc,
46771                    nulls_first,
46772                    explicit_asc: !desc,
46773                    with_fill: None,
46774                });
46775                if !self.match_token(TokenType::Comma) {
46776                    break;
46777                }
46778            }
46779            Some(orderings)
46780        } else {
46781            None
46782        };
46783
46784        // Parse optional SEPARATOR
46785        let separator = if self.match_token(TokenType::Separator) {
46786            self.parse_string()?
46787        } else {
46788            None
46789        };
46790
46791        Ok(Some(Expression::GroupConcat(Box::new(GroupConcatFunc {
46792            this: expr,
46793            separator,
46794            order_by,
46795            distinct,
46796            filter: None,
46797            inferred_type: None,
46798        }))))
46799    }
46800
46801    /// parse_grouping_set - Delegates to parse_grouping_sets
46802    #[allow(unused_variables, unused_mut)]
46803    pub fn parse_grouping_set(&mut self) -> Result<Option<Expression>> {
46804        self.parse_grouping_sets()
46805    }
46806
46807    /// parse_grouping_sets - Ported from Python _parse_grouping_sets
46808    /// Parses GROUPING SETS ((...), (...)) in GROUP BY
46809    #[allow(unused_variables, unused_mut)]
46810    pub fn parse_grouping_sets(&mut self) -> Result<Option<Expression>> {
46811        // Check for GROUPING SETS keyword
46812        if !self.match_text_seq(&["GROUPING", "SETS"]) {
46813            return Ok(None);
46814        }
46815
46816        // Parse wrapped grouping sets
46817        self.expect(TokenType::LParen)?;
46818        let mut expressions = Vec::new();
46819
46820        if !self.check(TokenType::RParen) {
46821            loop {
46822                // Each grouping set can be:
46823                // - A nested GROUPING SETS
46824                // - CUBE or ROLLUP
46825                // - A parenthesized list
46826                // - A single expression
46827                if let Some(nested) = self.parse_grouping_sets()? {
46828                    expressions.push(nested);
46829                } else if let Some(cube_rollup) = self.parse_cube_or_rollup()? {
46830                    expressions.push(cube_rollup);
46831                } else if self.match_token(TokenType::LParen) {
46832                    // Parenthesized group
46833                    let mut group = Vec::new();
46834                    if !self.check(TokenType::RParen) {
46835                        loop {
46836                            match self.parse_bitwise() {
46837                                Ok(Some(expr)) => group.push(expr),
46838                                Ok(None) => break,
46839                                Err(e) => return Err(e),
46840                            }
46841                            if !self.match_token(TokenType::Comma) {
46842                                break;
46843                            }
46844                        }
46845                    }
46846                    self.expect(TokenType::RParen)?;
46847                    expressions.push(Expression::Tuple(Box::new(Tuple { expressions: group })));
46848                } else {
46849                    // Single expression
46850                    match self.parse_bitwise() {
46851                        Ok(Some(expr)) => expressions.push(expr),
46852                        Ok(None) => break,
46853                        Err(e) => return Err(e),
46854                    }
46855                }
46856
46857                if !self.match_token(TokenType::Comma) {
46858                    break;
46859                }
46860            }
46861        }
46862
46863        self.expect(TokenType::RParen)?;
46864
46865        Ok(Some(Expression::GroupingSets(Box::new(GroupingSets {
46866            expressions,
46867        }))))
46868    }
46869
46870    /// parse_having - Parse HAVING clause
46871    /// Python: if not self._match(TokenType.HAVING): return None; return exp.Having(this=self._parse_disjunction())
46872    pub fn parse_having(&mut self) -> Result<Option<Expression>> {
46873        if !self.match_token(TokenType::Having) {
46874            return Ok(None);
46875        }
46876        // Parse the condition expression
46877        let condition = self.parse_expression()?;
46878        Ok(Some(Expression::Having(Box::new(Having {
46879            this: condition,
46880            comments: Vec::new(),
46881        }))))
46882    }
46883
46884    /// parse_having_max - Implemented from Python _parse_having_max
46885    /// Calls: parse_column
46886    #[allow(unused_variables, unused_mut)]
46887    pub fn parse_having_max(&mut self) -> Result<Option<Expression>> {
46888        if self.match_texts(&["MAX", "MIN"]) {
46889            // Matched one of: MAX, MIN
46890            return Ok(None);
46891        }
46892        Ok(None)
46893    }
46894
46895    /// parse_heredoc - Implemented from Python _parse_heredoc
46896    /// Parses dollar-quoted strings: $$content$$, $tag$content$tag$
46897    pub fn parse_heredoc(&mut self) -> Result<Option<Expression>> {
46898        // Check if current token is a HEREDOC_STRING type
46899        if self.match_token(TokenType::HeredocString) {
46900            let text = self.previous().text.clone();
46901            return Ok(Some(Expression::Heredoc(Box::new(Heredoc {
46902                this: Box::new(Expression::Literal(Box::new(Literal::String(text)))),
46903                tag: None,
46904            }))));
46905        }
46906
46907        // Try to parse $...$ or $tag$...$tag$
46908        if !self.match_text_seq(&["$"]) {
46909            return Ok(None);
46910        }
46911
46912        // Collect the tag text (if any) and the closing marker
46913        let mut tags = vec!["$".to_string()];
46914        let mut tag_text: Option<String> = None;
46915
46916        // Check if next token is connected (no whitespace) and collect tag
46917        if !self.is_at_end() {
46918            let next_text = self.peek().text.to_ascii_uppercase();
46919            if next_text == "$" {
46920                // Simple $$ ... $$ case
46921                self.skip();
46922                tags.push("$".to_string());
46923            } else {
46924                // $tag$ ... $tag$ case
46925                self.skip();
46926                tag_text = Some(next_text.clone());
46927                tags.push(next_text);
46928
46929                // Expect closing $
46930                if self.match_text_seq(&["$"]) {
46931                    tags.push("$".to_string());
46932                } else {
46933                    return Err(self.parse_error("No closing $ found"));
46934                }
46935            }
46936        }
46937
46938        // Now collect content until we find the closing tags
46939        let mut content_parts = Vec::new();
46940        let closing_tag = tags.join("");
46941
46942        while !self.is_at_end() {
46943            // Build current sequence to check for closing tag
46944            let current_text = self.peek().text.clone();
46945
46946            // Check if we've reached the closing tag
46947            if current_text == "$" || current_text.eq_ignore_ascii_case(&closing_tag) {
46948                // Try to match the full closing sequence
46949                let start_pos = self.current;
46950                let mut matched = true;
46951                for expected in &tags {
46952                    if self.is_at_end() || !self.peek().text.eq_ignore_ascii_case(expected) {
46953                        matched = false;
46954                        break;
46955                    }
46956                    self.skip();
46957                }
46958                if matched {
46959                    // Found the closing tag
46960                    let content = content_parts.join(" ");
46961                    return Ok(Some(Expression::Heredoc(Box::new(Heredoc {
46962                        this: Box::new(Expression::Literal(Box::new(Literal::String(content)))),
46963                        tag: tag_text
46964                            .map(|t| Box::new(Expression::Literal(Box::new(Literal::String(t))))),
46965                    }))));
46966                }
46967                // Not the closing tag, backtrack and add to content
46968                self.current = start_pos;
46969            }
46970
46971            content_parts.push(self.advance().text.clone());
46972        }
46973
46974        Err(self.parse_error(&format!("No closing {} found", closing_tag)))
46975    }
46976
46977    /// parse_hint_body - Delegates to parse_hint_fallback_to_string
46978    #[allow(unused_variables, unused_mut)]
46979    pub fn parse_hint_body(&mut self) -> Result<Option<Expression>> {
46980        self.parse_hint_fallback_to_string()
46981    }
46982
46983    /// parse_hint_fallback_to_string - Parses remaining hint tokens as a raw string
46984    /// Python: _parse_hint_fallback_to_string
46985    /// Used when structured hint parsing fails - collects all remaining tokens
46986    pub fn parse_hint_fallback_to_string(&mut self) -> Result<Option<Expression>> {
46987        // Collect all remaining tokens as a string
46988        let mut parts = Vec::new();
46989        while !self.is_at_end() {
46990            let token = self.advance();
46991            parts.push(token.text.clone());
46992        }
46993
46994        if parts.is_empty() {
46995            return Ok(None);
46996        }
46997
46998        let hint_text = parts.join(" ");
46999        Ok(Some(Expression::Hint(Box::new(Hint {
47000            expressions: vec![HintExpression::Raw(hint_text)],
47001        }))))
47002    }
47003
47004    /// parse_hint_function_call - Delegates to parse_function_call
47005    #[allow(unused_variables, unused_mut)]
47006    pub fn parse_hint_function_call(&mut self) -> Result<Option<Expression>> {
47007        self.parse_function_call()
47008    }
47009
47010    /// parse_historical_data - Snowflake AT/BEFORE time travel clauses
47011    /// Parses: AT(TIMESTAMP => expr) or BEFORE(STATEMENT => 'id') etc.
47012    /// Reference: https://docs.snowflake.com/en/sql-reference/constructs/at-before
47013    #[allow(unused_variables, unused_mut)]
47014    pub fn parse_historical_data(&mut self) -> Result<Option<Expression>> {
47015        // Save position for backtracking
47016        let start_index = self.current;
47017
47018        // Check for AT, BEFORE, or END keywords
47019        let this = if self.match_texts(&["AT", "BEFORE", "END"]) {
47020            self.previous().text.to_ascii_uppercase()
47021        } else {
47022            return Ok(None);
47023        };
47024
47025        // Expect opening paren and kind (OFFSET, STATEMENT, STREAM, TIMESTAMP, VERSION)
47026        if !self.match_token(TokenType::LParen) {
47027            // Backtrack if not the right pattern
47028            self.current = start_index;
47029            return Ok(None);
47030        }
47031
47032        let kind = if self.match_texts(&["OFFSET", "STATEMENT", "STREAM", "TIMESTAMP", "VERSION"]) {
47033            self.previous().text.to_ascii_uppercase()
47034        } else {
47035            // Backtrack if not the right pattern
47036            self.current = start_index;
47037            return Ok(None);
47038        };
47039
47040        // Expect => and expression
47041        if !self.match_token(TokenType::FArrow) {
47042            self.current = start_index;
47043            return Ok(None);
47044        }
47045
47046        let expression = self.parse_bitwise()?;
47047        if expression.is_none() {
47048            self.current = start_index;
47049            return Ok(None);
47050        }
47051
47052        self.match_token(TokenType::RParen); // Consume closing paren
47053
47054        Ok(Some(Expression::HistoricalData(Box::new(HistoricalData {
47055            this: Box::new(Expression::Identifier(Identifier::new(this))),
47056            kind,
47057            expression: Box::new(expression.unwrap()),
47058        }))))
47059    }
47060
47061    /// parse_id_var - Ported from Python _parse_id_var
47062    /// Parses an identifier or variable (more permissive than parse_identifier)
47063    #[allow(unused_variables, unused_mut)]
47064    pub fn parse_id_var(&mut self) -> Result<Option<Expression>> {
47065        // First try to parse a regular identifier
47066        if let Some(ident) = self.parse_identifier()? {
47067            return Ok(Some(ident));
47068        }
47069
47070        // Try to match Var token type
47071        if self.match_token(TokenType::Var) {
47072            let text = self.previous().text.clone();
47073            return Ok(Some(Expression::Identifier(Identifier {
47074                name: text,
47075                quoted: false,
47076                trailing_comments: Vec::new(),
47077                span: None,
47078            })));
47079        }
47080
47081        // Try to match string as identifier (some dialects allow this)
47082        if self.match_token(TokenType::String) {
47083            let text = self.previous().text.clone();
47084            return Ok(Some(Expression::Identifier(Identifier {
47085                name: text,
47086                quoted: true,
47087                trailing_comments: Vec::new(),
47088                span: None,
47089            })));
47090        }
47091
47092        // Accept keywords as identifiers in some contexts
47093        if self.check(TokenType::Select)
47094            || self.check(TokenType::From)
47095            || self.check(TokenType::Where)
47096            || self.check(TokenType::And)
47097            || self.check(TokenType::Or)
47098            || self.check(TokenType::Not)
47099            || self.check(TokenType::True)
47100            || self.check(TokenType::False)
47101            || self.check(TokenType::Null)
47102        {
47103            // Don't consume keywords as identifiers in parse_id_var
47104            return Ok(None);
47105        }
47106
47107        Ok(None)
47108    }
47109
47110    /// parse_identifier - Parse quoted identifier
47111    /// Python: if self._match(TokenType.IDENTIFIER): return self._identifier_expression(quoted=True)
47112    pub fn parse_identifier(&mut self) -> Result<Option<Expression>> {
47113        // Match quoted identifiers (e.g., "column_name" or `column_name`)
47114        if self.match_token(TokenType::QuotedIdentifier) || self.match_token(TokenType::Identifier)
47115        {
47116            let text = self.previous().text.clone();
47117            let quoted = self.previous().token_type == TokenType::QuotedIdentifier;
47118            return Ok(Some(Expression::Identifier(Identifier {
47119                name: text,
47120                quoted,
47121                trailing_comments: Vec::new(),
47122                span: None,
47123            })));
47124        }
47125        Ok(None)
47126    }
47127
47128    /// Parse IF expression
47129    /// IF(condition, true_value, false_value) - function style
47130    /// IF condition THEN true_value ELSE false_value END - statement style
47131    pub fn parse_if(&mut self) -> Result<Option<Expression>> {
47132        // TSQL/Fabric: IF (cond) BEGIN ... END is a statement, not a function.
47133        // Parse condition, strip outer parens, then capture rest as command.
47134        if matches!(
47135            self.config.dialect,
47136            Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)
47137        ) && self.check(TokenType::LParen)
47138        {
47139            // Parse the parenthesized condition using balanced paren matching
47140            let cond_start = self.current;
47141            self.skip(); // consume opening (
47142            let mut depth = 1;
47143            while depth > 0 && !self.is_at_end() {
47144                if self.check(TokenType::LParen) {
47145                    depth += 1;
47146                } else if self.check(TokenType::RParen) {
47147                    depth -= 1;
47148                    if depth == 0 {
47149                        break;
47150                    }
47151                }
47152                self.skip();
47153            }
47154            // Extract condition text from source (inside outer parens)
47155            let cond_text = if let Some(ref source) = self.source {
47156                let inner_start = self.tokens[cond_start + 1].span.start;
47157                let inner_end = self.tokens[self.current].span.start;
47158                source[inner_start..inner_end].trim().to_string()
47159            } else {
47160                self.tokens_to_sql(cond_start + 1, self.current)
47161            };
47162            self.skip(); // consume closing )
47163
47164            // Now collect the rest (BEGIN...END) as raw text
47165            let body_start = self.current;
47166            while !self.is_at_end() && !self.check(TokenType::Semicolon) {
47167                self.skip();
47168            }
47169            let body_text = if let Some(ref source) = self.source {
47170                let start_span = self.tokens[body_start].span.start;
47171                let end_span = if self.current > 0 {
47172                    self.tokens[self.current - 1].span.end
47173                } else {
47174                    start_span
47175                };
47176                source[start_span..end_span].trim().to_string()
47177            } else {
47178                self.tokens_to_sql(body_start, self.current)
47179            };
47180            let command_text = format!("IF {} {}", cond_text, body_text);
47181            return Ok(Some(Expression::Command(Box::new(
47182                crate::expressions::Command { this: command_text },
47183            ))));
47184        }
47185
47186        // Function style: IF(cond, true, false)
47187        if self.match_token(TokenType::LParen) {
47188            // ClickHouse: if() with zero args is valid (used in test queries)
47189            if self.check(TokenType::RParen) {
47190                self.skip(); // consume RParen
47191                return Ok(Some(Expression::Function(Box::new(Function {
47192                    name: "IF".to_string(),
47193                    args: vec![],
47194                    distinct: false,
47195                    trailing_comments: Vec::new(),
47196                    use_bracket_syntax: false,
47197                    no_parens: false,
47198                    quoted: false,
47199                    span: None,
47200                    inferred_type: None,
47201                }))));
47202            }
47203            let args = self.parse_expression_list()?;
47204            self.expect(TokenType::RParen)?;
47205
47206            if args.len() == 3 {
47207                return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
47208                    original_name: None,
47209                    condition: args[0].clone(),
47210                    true_value: args[1].clone(),
47211                    false_value: Some(args[2].clone()),
47212                    inferred_type: None,
47213                }))));
47214            } else if args.len() == 2 {
47215                return Ok(Some(Expression::IfFunc(Box::new(IfFunc {
47216                    original_name: None,
47217                    condition: args[0].clone(),
47218                    true_value: args[1].clone(),
47219                    false_value: None,
47220                    inferred_type: None,
47221                }))));
47222            } else if args.len() == 1 {
47223                return Ok(Some(Expression::Function(Box::new(Function {
47224                    name: "IF".to_string(),
47225                    args,
47226                    distinct: false,
47227                    trailing_comments: Vec::new(),
47228                    use_bracket_syntax: false,
47229                    no_parens: false,
47230                    quoted: false,
47231                    span: None,
47232                    inferred_type: None,
47233                }))));
47234            } else {
47235                return Err(self.parse_error("IF function requires 2 or 3 arguments"));
47236            }
47237        }
47238
47239        // TSQL: IF OBJECT_ID(...) IS NOT NULL [BEGIN] DROP TABLE x [; END] -> DROP TABLE IF EXISTS x
47240        if matches!(
47241            self.config.dialect,
47242            Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)
47243        ) {
47244            let saved = self.current;
47245            if self.match_text_seq(&["OBJECT_ID"]) {
47246                // Capture the OBJECT_ID arguments text for TSQL round-trip
47247                let object_id_args_text = if self.match_token(TokenType::LParen) {
47248                    let args_start = self.current;
47249                    let args = self.parse_expression_list()?;
47250                    // Reconstruct args text from source
47251                    let args_text = if let Some(ref source) = self.source {
47252                        let start_span = self.tokens[args_start].span.start;
47253                        let end_span = self.tokens[self.current].span.start;
47254                        source[start_span..end_span].trim().to_string()
47255                    } else {
47256                        // Fallback: generate from parsed expressions
47257                        args.iter()
47258                            .map(|a| format!("{:?}", a))
47259                            .collect::<Vec<_>>()
47260                            .join(", ")
47261                    };
47262                    let _ = self.match_token(TokenType::RParen);
47263                    Some(args_text)
47264                } else {
47265                    None
47266                };
47267                if self.match_text_seq(&["IS", "NOT", "NULL"]) {
47268                    // Check for DROP directly or BEGIN ... DROP ... END
47269                    let has_begin = self.match_token(TokenType::Begin);
47270                    if self.check(TokenType::Drop) {
47271                        // Parse DROP TABLE, forcing if_exists = true
47272                        self.skip(); // consume DROP
47273                        if self.match_token(TokenType::Table) {
47274                            // Parse table names
47275                            let mut names = Vec::new();
47276                            loop {
47277                                names.push(self.parse_table_ref()?);
47278                                if !self.match_token(TokenType::Comma) {
47279                                    break;
47280                                }
47281                            }
47282                            // If we had BEGIN, consume optional ; and END
47283                            if has_begin {
47284                                let _ = self.match_token(TokenType::Semicolon);
47285                                let _ = self.match_token(TokenType::End);
47286                            }
47287                            return Ok(Some(Expression::DropTable(Box::new(
47288                                crate::expressions::DropTable {
47289                                    names,
47290                                    if_exists: true,
47291                                    cascade: false,
47292                                    cascade_constraints: false,
47293                                    purge: false,
47294                                    leading_comments: Vec::new(),
47295                                    object_id_args: object_id_args_text,
47296                                    sync: false,
47297                                    iceberg: false,
47298                                    restrict: false,
47299                                },
47300                            ))));
47301                        }
47302                    }
47303                }
47304                // Retreat if pattern didn't match
47305                self.current = saved;
47306            }
47307        }
47308
47309        // Statement style: IF cond THEN true [ELSE false] END/ENDIF
47310        // Use parse_disjunction (parse_or) for condition - same as Python sqlglot
47311        // This ensures we stop at THEN rather than consuming too much
47312        let condition = match self.parse_disjunction()? {
47313            Some(c) => c,
47314            None => return Ok(None),
47315        };
47316
47317        if !self.match_token(TokenType::Then) {
47318            // Not statement style, return as just the expression parsed
47319            return Ok(Some(condition));
47320        }
47321
47322        // Parse true value - use parse_disjunction to stop at ELSE/END
47323        let true_value = match self.parse_disjunction()? {
47324            Some(v) => v,
47325            None => return Err(self.parse_error("Expected expression after THEN")),
47326        };
47327
47328        let false_value = if self.match_token(TokenType::Else) {
47329            match self.parse_disjunction()? {
47330                Some(v) => Some(v),
47331                None => return Err(self.parse_error("Expected expression after ELSE")),
47332            }
47333        } else {
47334            None
47335        };
47336
47337        // Consume END or ENDIF (Exasol tokenizes ENDIF as END)
47338        self.match_token(TokenType::End);
47339
47340        Ok(Some(Expression::IfFunc(Box::new(IfFunc {
47341            original_name: None,
47342            condition,
47343            true_value,
47344            false_value,
47345            inferred_type: None,
47346        }))))
47347    }
47348
47349    /// parse_in - Ported from Python _parse_in
47350    /// Parses IN expression: expr IN (values...) or expr IN (subquery)
47351    /// Can also parse standalone IN list after IN keyword has been matched
47352    #[allow(unused_variables, unused_mut)]
47353    pub fn parse_in(&mut self) -> Result<Option<Expression>> {
47354        // If we're at IN keyword, parse what follows
47355        if self.match_token(TokenType::In) {
47356            return Err(self.parse_error("Expected expression before IN"));
47357        }
47358
47359        // Try to parse as a complete expression: left IN (...)
47360        let saved_pos = self.current;
47361
47362        // Parse the left side expression
47363        match self.parse_bitwise() {
47364            Ok(Some(left_expr)) => {
47365                // Check for optional NOT
47366                let negate = self.match_token(TokenType::Not);
47367
47368                // Expect IN keyword
47369                if self.match_token(TokenType::In) {
47370                    let in_result = self.parse_in_with_expr(Some(left_expr))?;
47371                    return Ok(Some(if negate {
47372                        Expression::Not(Box::new(UnaryOp {
47373                            this: in_result,
47374                            inferred_type: None,
47375                        }))
47376                    } else {
47377                        in_result
47378                    }));
47379                }
47380
47381                // Not an IN expression, restore position
47382                self.current = saved_pos;
47383                Ok(None)
47384            }
47385            Ok(None) => {
47386                self.current = saved_pos;
47387                Ok(None)
47388            }
47389            Err(_) => {
47390                self.current = saved_pos;
47391                Ok(None)
47392            }
47393        }
47394    }
47395
47396    /// parse_index - Implemented from Python _parse_index
47397    /// Calls: parse_index_params, parse_id_var
47398    #[allow(unused_variables, unused_mut)]
47399    pub fn parse_index(&mut self) -> Result<Option<Expression>> {
47400        if self.match_text_seq(&["PRIMARY"]) {
47401            return Ok(Some(Expression::Index(Box::new(Index {
47402                this: None,
47403                table: None,
47404                unique: false,
47405                primary: None,
47406                amp: None,
47407                params: Vec::new(),
47408            }))));
47409        }
47410        if self.match_text_seq(&["AMP"]) {
47411            // Matched: AMP
47412            return Ok(None);
47413        }
47414        Ok(None)
47415    }
47416
47417    /// parse_index_params - Implemented from Python _parse_index_params
47418    /// Calls: parse_where, parse_wrapped_properties, parse_wrapped_id_vars
47419    #[allow(unused_variables, unused_mut)]
47420    pub fn parse_index_params(&mut self) -> Result<Option<Expression>> {
47421        if self.match_text_seq(&["INCLUDE"]) {
47422            return Ok(Some(Expression::IndexParameters(Box::new(
47423                IndexParameters {
47424                    using: None,
47425                    include: None,
47426                    columns: Vec::new(),
47427                    with_storage: None,
47428                    partition_by: None,
47429                    tablespace: None,
47430                    where_: None,
47431                    on: None,
47432                },
47433            ))));
47434        }
47435        if self.match_text_seq(&["USING", "INDEX", "TABLESPACE"]) {
47436            // Matched: USING INDEX TABLESPACE
47437            return Ok(None);
47438        }
47439        Ok(None)
47440    }
47441
47442    /// parse_initcap - Ported from Python _parse_initcap
47443    #[allow(unused_variables, unused_mut)]
47444    /// parse_initcap - Parses INITCAP function
47445    /// Example: INITCAP(str) or INITCAP(str, delimiter)
47446    pub fn parse_initcap(&mut self) -> Result<Option<Expression>> {
47447        // Parse the first argument (string to capitalize)
47448        let args = self.parse_expression_list()?;
47449
47450        if args.is_empty() {
47451            return Ok(None);
47452        }
47453
47454        // Initcap is a UnaryFunc
47455        Ok(Some(Expression::Initcap(Box::new(UnaryFunc::new(
47456            args.into_iter().next().unwrap(),
47457        )))))
47458    }
47459
47460    /// parse_inline - Implemented from Python _parse_inline
47461    #[allow(unused_variables, unused_mut)]
47462    pub fn parse_inline(&mut self) -> Result<Option<Expression>> {
47463        if self.match_text_seq(&["LENGTH"]) {
47464            // Matched: LENGTH
47465            return Ok(None);
47466        }
47467        Ok(None)
47468    }
47469
47470    /// parse_insert_table - Parse table reference for INSERT statement
47471    /// Parses: table_name [schema] [partition] [alias]
47472    /// This method is a simple wrapper around parse_table for INSERT context
47473    #[allow(unused_variables, unused_mut)]
47474    pub fn parse_insert_table(&mut self) -> Result<Option<Expression>> {
47475        // Parse the table reference - parse_table handles aliases
47476        self.parse_table()
47477    }
47478
47479    /// parse_interpolate - Implemented from Python _parse_interpolate
47480    /// Parses INTERPOLATE clause for ClickHouse ORDER BY WITH FILL
47481    pub fn parse_interpolate(&mut self) -> Result<Option<Expression>> {
47482        if !self.match_text_seq(&["INTERPOLATE"]) {
47483            return Ok(None);
47484        }
47485
47486        // Parse wrapped CSV of name-as-expression pairs
47487        if self.match_token(TokenType::LParen) {
47488            let mut expressions = Vec::new();
47489            loop {
47490                if let Some(expr) = self.parse_name_as_expression()? {
47491                    expressions.push(expr);
47492                }
47493                if !self.match_token(TokenType::Comma) {
47494                    break;
47495                }
47496            }
47497            self.match_token(TokenType::RParen);
47498
47499            if expressions.is_empty() {
47500                return Ok(None);
47501            }
47502
47503            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
47504        }
47505
47506        Ok(None)
47507    }
47508
47509    /// parse_interval - Creates Interval expression
47510    /// Parses INTERVAL expressions: INTERVAL '1 day', INTERVAL 1 MONTH, etc.
47511    #[allow(unused_variables, unused_mut)]
47512    pub fn parse_interval(&mut self) -> Result<Option<Expression>> {
47513        // Delegate to the existing try_parse_interval method
47514        self.try_parse_interval()
47515    }
47516
47517    /// parse_interval_span - Implemented from Python _parse_interval_span
47518    /// Calls: parse_function
47519    #[allow(unused_variables, unused_mut)]
47520    pub fn parse_interval_span(&mut self) -> Result<Option<Expression>> {
47521        if self.match_text_seq(&["TO"]) {
47522            return Ok(Some(Expression::Var(Box::new(Var {
47523                this: String::new(),
47524            }))));
47525        }
47526        if self.match_text_seq(&["TO"]) {
47527            // Matched: TO
47528            return Ok(None);
47529        }
47530        Ok(None)
47531    }
47532
47533    /// parse_into - Implemented from Python _parse_into
47534    /// Parses: INTO [TEMPORARY] [UNLOGGED] [TABLE] table_name
47535    /// Returns the table expression for the INTO clause
47536    #[allow(unused_variables, unused_mut)]
47537    pub fn parse_into(&mut self) -> Result<Option<Expression>> {
47538        if !self.match_token(TokenType::Into) {
47539            return Ok(None);
47540        }
47541
47542        // Optional TEMPORARY
47543        let _temp = self.match_token(TokenType::Temporary);
47544
47545        // Optional UNLOGGED
47546        let _unlogged = self.match_text_seq(&["UNLOGGED"]);
47547
47548        // Optional TABLE keyword
47549        let _ = self.match_token(TokenType::Table);
47550
47551        // Parse the table name
47552        self.parse_table_parts()
47553    }
47554
47555    /// parse_introducer - Parses MySQL introducer expression (_charset'string')
47556    /// Python: _parse_introducer
47557    /// Format: _charset 'literal'
47558    pub fn parse_introducer(&mut self) -> Result<Option<Expression>> {
47559        // We expect to have already consumed the introducer token (e.g., _utf8)
47560        let token = self.previous().clone();
47561
47562        // Try to parse a primary expression (usually a string literal)
47563        // parse_primary returns Expression (not Option), so we use it directly
47564        let literal = self.parse_primary()?;
47565
47566        // Check if it's a null expression (indicating nothing was parsed)
47567        match &literal {
47568            Expression::Null(_) => {
47569                // Just return as an identifier
47570                Ok(Some(Expression::Identifier(Identifier {
47571                    name: token.text.clone(),
47572                    quoted: false,
47573                    trailing_comments: Vec::new(),
47574                    span: None,
47575                })))
47576            }
47577            _ => Ok(Some(Expression::Introducer(Box::new(Introducer {
47578                this: Box::new(Expression::Identifier(Identifier {
47579                    name: token.text.clone(),
47580                    quoted: false,
47581                    trailing_comments: Vec::new(),
47582                    span: None,
47583                })),
47584                expression: Box::new(literal),
47585            })))),
47586        }
47587    }
47588
47589    /// parse_is - Implemented from Python _parse_is
47590    /// Calls: parse_null, parse_bitwise
47591    #[allow(unused_variables, unused_mut)]
47592    pub fn parse_is(&mut self) -> Result<Option<Expression>> {
47593        if self.match_text_seq(&["DISTINCT", "FROM"]) {
47594            return Ok(Some(Expression::JSON(Box::new(JSON {
47595                this: None,
47596                with_: None,
47597                unique: false,
47598            }))));
47599        }
47600        if self.match_text_seq(&["WITH"]) {
47601            // Matched: WITH
47602            return Ok(None);
47603        }
47604        if self.match_text_seq(&["WITHOUT"]) {
47605            // Matched: WITHOUT
47606            return Ok(None);
47607        }
47608        Ok(None)
47609    }
47610
47611    /// parse_join - Ported from Python _parse_join
47612    /// Parses a single JOIN clause: [method] [side] [kind] JOIN table [ON condition | USING (columns)]
47613    /// Returns the Join wrapped in an Expression, or None if no join is found
47614    #[allow(unused_variables, unused_mut)]
47615    pub fn parse_join(&mut self) -> Result<Option<Expression>> {
47616        // Check for comma-style implicit join
47617        if self.match_token(TokenType::Comma) {
47618            if let Ok(Some(table)) = self.parse_table() {
47619                return Ok(Some(Expression::Join(Box::new(Join {
47620                    this: table,
47621                    on: None,
47622                    using: Vec::new(),
47623                    kind: JoinKind::Implicit,
47624                    use_inner_keyword: false,
47625                    use_outer_keyword: false,
47626                    deferred_condition: false,
47627                    join_hint: None,
47628                    match_condition: None,
47629                    pivots: Vec::new(),
47630                    comments: Vec::new(),
47631                    nesting_group: 0,
47632                    directed: false,
47633                }))));
47634            }
47635            return Ok(None);
47636        }
47637
47638        // Try to parse join kind (INNER, LEFT, RIGHT, FULL, CROSS, etc.)
47639        let saved_pos = self.current;
47640        if let Some((kind, needs_join_keyword, use_inner_keyword, use_outer_keyword, join_hint)) =
47641            self.try_parse_join_kind()
47642        {
47643            // Collect comments from tokens consumed by try_parse_join_kind
47644            let mut join_comments = Vec::new();
47645            for i in saved_pos..self.current {
47646                if i < self.tokens.len() {
47647                    join_comments.extend(self.tokens[i].trailing_comments.iter().cloned());
47648                }
47649            }
47650
47651            // If kind requires JOIN keyword, expect it
47652            if needs_join_keyword && !self.match_token(TokenType::Join) {
47653                self.current = saved_pos;
47654                return Ok(None);
47655            }
47656
47657            // Parse the table being joined
47658            let table = self.parse_table_expression()?;
47659
47660            // Parse ON or USING condition
47661            let (on, using) = if self.match_token(TokenType::On) {
47662                (Some(self.parse_expression()?), Vec::new())
47663            } else if self.match_token(TokenType::Using) {
47664                let has_parens = self.match_token(TokenType::LParen);
47665                // Use parse_using_column_list to handle qualified names like t1.col
47666                let cols = self.parse_using_column_list()?;
47667                if has_parens {
47668                    self.expect(TokenType::RParen)?;
47669                }
47670                (None, cols)
47671            } else {
47672                (None, Vec::new())
47673            };
47674
47675            return Ok(Some(Expression::Join(Box::new(Join {
47676                this: table,
47677                on,
47678                using,
47679                kind,
47680                use_inner_keyword,
47681                use_outer_keyword,
47682                deferred_condition: false,
47683                join_hint,
47684                match_condition: None,
47685                pivots: Vec::new(),
47686                comments: join_comments,
47687                nesting_group: 0,
47688                directed: false,
47689            }))));
47690        }
47691
47692        // Check for CROSS APPLY / OUTER APPLY (SQL Server)
47693        if self.match_text_seq(&["CROSS", "APPLY"]) || self.match_text_seq(&["OUTER", "APPLY"]) {
47694            let is_outer = self.previous().text.eq_ignore_ascii_case("OUTER");
47695            let table = self.parse_table_expression()?;
47696            return Ok(Some(Expression::Join(Box::new(Join {
47697                this: table,
47698                on: None,
47699                using: Vec::new(),
47700                kind: if is_outer {
47701                    JoinKind::Outer
47702                } else {
47703                    JoinKind::Cross
47704                },
47705                use_inner_keyword: false,
47706                use_outer_keyword: is_outer,
47707                deferred_condition: false,
47708                join_hint: None,
47709                match_condition: None,
47710                pivots: Vec::new(),
47711                comments: Vec::new(),
47712                nesting_group: 0,
47713                directed: false,
47714            }))));
47715        }
47716
47717        Ok(None)
47718    }
47719
47720    /// parse_join_hint - Spark/Hive join hints (BROADCAST, MERGE, SHUFFLE_HASH, etc.)
47721    /// Parses: HINT_NAME(table1, table2, ...)
47722    /// hint_name should be the already matched hint keyword (BROADCAST, MAPJOIN, etc.)
47723    #[allow(unused_variables, unused_mut)]
47724    pub fn parse_join_hint(&mut self, hint_name: &str) -> Result<Option<Expression>> {
47725        // Parse comma-separated list of tables
47726        let mut tables = Vec::new();
47727        loop {
47728            if let Some(table) = self.parse_table()? {
47729                tables.push(table);
47730            } else {
47731                break;
47732            }
47733            if !self.match_token(TokenType::Comma) {
47734                break;
47735            }
47736        }
47737
47738        Ok(Some(Expression::JoinHint(Box::new(JoinHint {
47739            this: Box::new(Expression::Identifier(Identifier::new(
47740                hint_name.to_ascii_uppercase(),
47741            ))),
47742            expressions: tables,
47743        }))))
47744    }
47745
47746    /// parse_join_parts - Ported from Python _parse_join_parts
47747    /// Returns (method, side, kind) where each is an optional string
47748    /// method: ASOF, NATURAL, POSITIONAL
47749    /// side: LEFT, RIGHT, FULL
47750    /// kind: ANTI, CROSS, INNER, OUTER, SEMI
47751    pub fn parse_join_parts(&mut self) -> (Option<String>, Option<String>, Option<String>) {
47752        // Parse join method (ASOF, NATURAL, POSITIONAL)
47753        let method = if self.match_texts(&["ASOF", "NATURAL", "POSITIONAL"]) {
47754            Some(self.previous().text.to_ascii_uppercase())
47755        } else {
47756            None
47757        };
47758
47759        // Parse join side (LEFT, RIGHT, FULL)
47760        let side = if self.match_texts(&["LEFT", "RIGHT", "FULL"]) {
47761            Some(self.previous().text.to_ascii_uppercase())
47762        } else {
47763            None
47764        };
47765
47766        // Parse join kind (ANTI, CROSS, INNER, OUTER, SEMI)
47767        let kind = if self.match_texts(&["ANTI", "CROSS", "INNER", "OUTER", "SEMI"]) {
47768            Some(self.previous().text.to_ascii_uppercase())
47769        } else if self.match_token(TokenType::StraightJoin) {
47770            Some("STRAIGHT_JOIN".to_string())
47771        } else {
47772            None
47773        };
47774
47775        (method, side, kind)
47776    }
47777
47778    /// parse_journal - Parses JOURNAL property (Teradata)
47779    /// Python: _parse_journal
47780    /// Creates a JournalProperty expression
47781    pub fn parse_journal(&mut self) -> Result<Option<Expression>> {
47782        self.parse_journal_impl(false, false, false, false, false)
47783    }
47784
47785    /// Implementation of parse_journal with options
47786    pub fn parse_journal_impl(
47787        &mut self,
47788        no: bool,
47789        dual: bool,
47790        before: bool,
47791        local: bool,
47792        after: bool,
47793    ) -> Result<Option<Expression>> {
47794        Ok(Some(Expression::JournalProperty(Box::new(
47795            JournalProperty {
47796                no: if no {
47797                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47798                        value: true,
47799                    })))
47800                } else {
47801                    None
47802                },
47803                dual: if dual {
47804                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47805                        value: true,
47806                    })))
47807                } else {
47808                    None
47809                },
47810                before: if before {
47811                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47812                        value: true,
47813                    })))
47814                } else {
47815                    None
47816                },
47817                local: if local {
47818                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47819                        value: true,
47820                    })))
47821                } else {
47822                    None
47823                },
47824                after: if after {
47825                    Some(Box::new(Expression::Boolean(BooleanLiteral {
47826                        value: true,
47827                    })))
47828                } else {
47829                    None
47830                },
47831            },
47832        ))))
47833    }
47834
47835    /// parse_json_column_def - Implemented from Python _parse_json_column_def
47836    /// Calls: parse_string, parse_json_schema, parse_id_var
47837    #[allow(unused_variables, unused_mut)]
47838    pub fn parse_json_column_def(&mut self) -> Result<Option<Expression>> {
47839        if self.match_text_seq(&["NESTED"]) {
47840            return Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
47841                this: None,
47842                kind: None,
47843                path: None,
47844                nested_schema: None,
47845                ordinality: None,
47846            }))));
47847        }
47848        if self.match_text_seq(&["PATH"]) {
47849            // Matched: PATH
47850            return Ok(None);
47851        }
47852        Ok(None)
47853    }
47854
47855    /// parse_json_key_value - Implemented from Python _parse_json_key_value
47856    #[allow(unused_variables, unused_mut)]
47857    /// parse_json_key_value - Parses a JSON key-value pair
47858    /// Python: _parse_json_key_value
47859    /// Format: [KEY] key [: | VALUE] value
47860    pub fn parse_json_key_value(&mut self) -> Result<Option<Expression>> {
47861        // Optional KEY keyword
47862        self.match_text_seq(&["KEY"]);
47863
47864        // Parse the key expression
47865        let key = self.parse_column()?;
47866
47867        // Match separator (colon, comma, or VALUE keyword)
47868        let _ = self.match_token(TokenType::Colon)
47869            || self.match_token(TokenType::Comma)
47870            || self.match_text_seq(&["VALUE"]);
47871
47872        // Optional VALUE keyword
47873        self.match_text_seq(&["VALUE"]);
47874
47875        // Parse the value expression
47876        let value = self.parse_bitwise()?;
47877
47878        // If neither key nor value, return None
47879        match (key, value) {
47880            (None, None) => Ok(None),
47881            (Some(k), None) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
47882                this: Box::new(k),
47883                expression: Box::new(Expression::Null(Null)),
47884            })))),
47885            (None, Some(v)) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
47886                this: Box::new(Expression::Null(Null)),
47887                expression: Box::new(v),
47888            })))),
47889            (Some(k), Some(v)) => Ok(Some(Expression::JSONKeyValue(Box::new(JSONKeyValue {
47890                this: Box::new(k),
47891                expression: Box::new(v),
47892            })))),
47893        }
47894    }
47895
47896    /// parse_json_object - Parses JSON_OBJECT function
47897    /// Python: _parse_json_object
47898    /// Handles both JSON_OBJECT and JSON_OBJECTAGG
47899    pub fn parse_json_object(&mut self) -> Result<Option<Expression>> {
47900        self.parse_json_object_impl(false)
47901    }
47902
47903    /// Implementation of JSON object parsing with aggregate flag
47904    pub fn parse_json_object_impl(&mut self, agg: bool) -> Result<Option<Expression>> {
47905        // Try to parse a star expression
47906        let star = self.parse_star()?;
47907
47908        // Parse expressions: either star or comma-separated key-value pairs
47909        let expressions = if let Some(star_expr) = star {
47910            vec![star_expr]
47911        } else {
47912            // Parse comma-separated JSON key-value pairs
47913            let mut exprs = Vec::new();
47914            loop {
47915                if let Some(kv) = self.parse_json_key_value()? {
47916                    // Wrap with FORMAT JSON if specified
47917                    if self.match_text_seq(&["FORMAT", "JSON"]) {
47918                        exprs.push(Expression::JSONFormat(Box::new(JSONFormat {
47919                            this: Some(Box::new(kv)),
47920                            options: Vec::new(),
47921                            is_json: None,
47922                            to_json: None,
47923                        })));
47924                    } else {
47925                        exprs.push(kv);
47926                    }
47927                } else {
47928                    break;
47929                }
47930                if !self.match_token(TokenType::Comma) {
47931                    break;
47932                }
47933            }
47934            exprs
47935        };
47936
47937        // Parse NULL handling: NULL ON NULL or ABSENT ON NULL
47938        let null_handling = self.parse_json_on_null_handling()?;
47939
47940        // Parse UNIQUE KEYS option
47941        let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE"]) {
47942            self.match_text_seq(&["KEYS"]);
47943            Some(Box::new(Expression::Boolean(BooleanLiteral {
47944                value: true,
47945            })))
47946        } else if self.match_text_seq(&["WITHOUT", "UNIQUE"]) {
47947            self.match_text_seq(&["KEYS"]);
47948            Some(Box::new(Expression::Boolean(BooleanLiteral {
47949                value: false,
47950            })))
47951        } else {
47952            None
47953        };
47954
47955        // Consume optional KEYS keyword
47956        self.match_text_seq(&["KEYS"]);
47957
47958        // Parse RETURNING clause
47959        let return_type = if self.match_text_seq(&["RETURNING"]) {
47960            let type_expr = self.parse_type()?;
47961            // Wrap with FORMAT JSON if specified
47962            if self.match_text_seq(&["FORMAT", "JSON"]) {
47963                type_expr.map(|t| {
47964                    Box::new(Expression::JSONFormat(Box::new(JSONFormat {
47965                        this: Some(Box::new(t)),
47966                        options: Vec::new(),
47967                        is_json: None,
47968                        to_json: None,
47969                    })))
47970                })
47971            } else {
47972                type_expr.map(Box::new)
47973            }
47974        } else {
47975            None
47976        };
47977
47978        // Parse ENCODING option
47979        let encoding = if self.match_text_seq(&["ENCODING"]) {
47980            self.parse_var()?.map(Box::new)
47981        } else {
47982            None
47983        };
47984
47985        if agg {
47986            Ok(Some(Expression::JSONObjectAgg(Box::new(JSONObjectAgg {
47987                expressions,
47988                null_handling,
47989                unique_keys,
47990                return_type,
47991                encoding,
47992            }))))
47993        } else {
47994            Ok(Some(Expression::JSONObject(Box::new(JSONObject {
47995                expressions,
47996                null_handling,
47997                unique_keys,
47998                return_type,
47999                encoding,
48000            }))))
48001        }
48002    }
48003
48004    /// Parse JSON NULL handling clause: NULL ON NULL or ABSENT ON NULL
48005    fn parse_json_on_null_handling(&mut self) -> Result<Option<Box<Expression>>> {
48006        if self.match_text_seq(&["NULL", "ON", "NULL"]) {
48007            Ok(Some(Box::new(Expression::Var(Box::new(Var {
48008                this: "NULL ON NULL".to_string(),
48009            })))))
48010        } else if self.match_text_seq(&["ABSENT", "ON", "NULL"]) {
48011            Ok(Some(Box::new(Expression::Var(Box::new(Var {
48012                this: "ABSENT ON NULL".to_string(),
48013            })))))
48014        } else {
48015            Ok(None)
48016        }
48017    }
48018
48019    /// parse_json_schema - Implemented from Python _parse_json_schema
48020    #[allow(unused_variables, unused_mut)]
48021    pub fn parse_json_schema(&mut self) -> Result<Option<Expression>> {
48022        if self.match_text_seq(&["COLUMNS"]) {
48023            return Ok(Some(Expression::JSONSchema(Box::new(JSONSchema {
48024                expressions: Vec::new(),
48025            }))));
48026        }
48027        Ok(None)
48028    }
48029
48030    /// Parse JSON_TABLE COLUMNS clause: COLUMNS (column_def, column_def, ...) or COLUMNS column_def
48031    /// Column definitions can be:
48032    /// - name type PATH 'json_path'
48033    /// - name FOR ORDINALITY
48034    /// - NESTED [PATH] 'json_path' COLUMNS (...)
48035    pub fn parse_json_table_columns(&mut self) -> Result<Option<Expression>> {
48036        if !self.match_text_seq(&["COLUMNS"]) {
48037            return Ok(None);
48038        }
48039
48040        // Check for opening paren - Oracle supports both COLUMNS(...) and COLUMNS col PATH '...'
48041        let has_parens = self.match_token(TokenType::LParen);
48042
48043        let mut columns = Vec::new();
48044
48045        // Parse column definitions
48046        if has_parens {
48047            // COLUMNS(col1, col2, ...)
48048            if !self.check(TokenType::RParen) {
48049                loop {
48050                    if let Some(col_def) = self.parse_json_table_column_def()? {
48051                        columns.push(col_def);
48052                    }
48053                    if !self.match_token(TokenType::Comma) {
48054                        break;
48055                    }
48056                }
48057            }
48058            // Expect closing paren for COLUMNS(...)
48059            self.expect(TokenType::RParen)?;
48060        } else {
48061            // COLUMNS col PATH '...' (single column without parens)
48062            if let Some(col_def) = self.parse_json_table_column_def()? {
48063                columns.push(col_def);
48064            }
48065        }
48066
48067        Ok(Some(Expression::JSONSchema(Box::new(JSONSchema {
48068            expressions: columns,
48069        }))))
48070    }
48071
48072    /// Parse a single JSON_TABLE column definition
48073    /// Formats:
48074    /// - name [FOR ORDINALITY] [type] [PATH 'path']
48075    /// - NESTED [PATH] 'path' COLUMNS (...)
48076    pub fn parse_json_table_column_def(&mut self) -> Result<Option<Expression>> {
48077        // Check for NESTED column
48078        if self.match_text_seq(&["NESTED"]) {
48079            // NESTED [PATH] 'json_path' COLUMNS (...)
48080            self.match_text_seq(&["PATH"]); // Optional PATH keyword
48081            let path = self.parse_string()?;
48082            let nested_schema = self.parse_json_table_columns()?;
48083
48084            return Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
48085                this: None,
48086                kind: None,
48087                path: path.map(Box::new),
48088                nested_schema: nested_schema.map(Box::new),
48089                ordinality: None,
48090            }))));
48091        }
48092
48093        // Regular column: name [FOR ORDINALITY] [type] [PATH 'path']
48094        let name = self.parse_id_var()?;
48095        if name.is_none() {
48096            return Ok(None);
48097        }
48098
48099        // Check for FOR ORDINALITY
48100        let ordinality = if self.match_text_seq(&["FOR", "ORDINALITY"]) {
48101            Some(Box::new(Expression::Boolean(BooleanLiteral {
48102                value: true,
48103            })))
48104        } else {
48105            None
48106        };
48107
48108        // Parse data type (if not FOR ORDINALITY, type is expected)
48109        let kind = if ordinality.is_none() {
48110            // Try to parse a data type
48111            let data_type = self.parse_data_type_optional()?;
48112            data_type.map(|dt| self.data_type_to_string(&dt))
48113        } else {
48114            None
48115        };
48116
48117        // Parse PATH 'json_path'
48118        let path = if self.match_text_seq(&["PATH"]) {
48119            self.parse_string()?
48120        } else {
48121            None
48122        };
48123
48124        Ok(Some(Expression::JSONColumnDef(Box::new(JSONColumnDef {
48125            this: name.map(Box::new),
48126            kind,
48127            path: path.map(Box::new),
48128            nested_schema: None,
48129            ordinality,
48130        }))))
48131    }
48132
48133    /// Parse JSON_TABLE function
48134    /// JSON_TABLE(expr, path COLUMNS (...)) [ON ERROR ...] [ON EMPTY ...]
48135    pub fn parse_json_table(&mut self) -> Result<Option<Expression>> {
48136        // Parse the JSON expression
48137        let this = self.parse_expression()?;
48138
48139        // Optional path after comma
48140        let path = if self.match_token(TokenType::Comma) {
48141            if let Some(s) = self.parse_string()? {
48142                Some(Box::new(s))
48143            } else {
48144                None
48145            }
48146        } else {
48147            None
48148        };
48149
48150        // Parse error handling: ON ERROR NULL or ON ERROR ERROR
48151        let error_handling = if self.match_text_seq(&["ON", "ERROR"]) {
48152            if self.match_text_seq(&["NULL"]) {
48153                Some(Box::new(Expression::Var(Box::new(Var {
48154                    this: "NULL".to_string(),
48155                }))))
48156            } else if self.match_text_seq(&["ERROR"]) {
48157                Some(Box::new(Expression::Var(Box::new(Var {
48158                    this: "ERROR".to_string(),
48159                }))))
48160            } else {
48161                None
48162            }
48163        } else {
48164            None
48165        };
48166
48167        // Parse empty handling: ON EMPTY NULL or ON EMPTY ERROR
48168        let empty_handling = if self.match_text_seq(&["ON", "EMPTY"]) {
48169            if self.match_text_seq(&["NULL"]) {
48170                Some(Box::new(Expression::Var(Box::new(Var {
48171                    this: "NULL".to_string(),
48172                }))))
48173            } else if self.match_text_seq(&["ERROR"]) {
48174                Some(Box::new(Expression::Var(Box::new(Var {
48175                    this: "ERROR".to_string(),
48176                }))))
48177            } else {
48178                None
48179            }
48180        } else {
48181            None
48182        };
48183
48184        // Parse COLUMNS clause
48185        let schema = self.parse_json_schema()?;
48186
48187        Ok(Some(Expression::JSONTable(Box::new(JSONTable {
48188            this: Box::new(this),
48189            schema: schema.map(Box::new),
48190            path,
48191            error_handling,
48192            empty_handling,
48193        }))))
48194    }
48195
48196    /// parse_json_value - Ported from Python _parse_json_value
48197    #[allow(unused_variables, unused_mut)]
48198    /// parse_json_value - Parses JSON_VALUE function
48199    /// Example: JSON_VALUE(json, '$.path' RETURNING type)
48200    pub fn parse_json_value(&mut self) -> Result<Option<Expression>> {
48201        // Parse the JSON expression
48202        let this = self.parse_expression()?;
48203
48204        // Parse path (after comma)
48205        self.match_token(TokenType::Comma);
48206        let path = self.parse_expression()?;
48207
48208        // Parse optional RETURNING type
48209        let returning = if self.match_token(TokenType::Returning) {
48210            Some(Box::new(self.parse_expression()?))
48211        } else {
48212            None
48213        };
48214
48215        // Parse optional ON condition (ON ERROR, ON EMPTY)
48216        let on_condition = if self.check(TokenType::On) {
48217            self.parse_on_condition()?
48218        } else {
48219            None
48220        };
48221
48222        Ok(Some(Expression::JSONValue(Box::new(JSONValue {
48223            this: Box::new(this),
48224            path: Some(Box::new(path)),
48225            returning,
48226            on_condition: on_condition.map(Box::new),
48227        }))))
48228    }
48229
48230    /// parse_key_constraint_options - Implemented from Python _parse_key_constraint_options
48231    #[allow(unused_variables, unused_mut)]
48232    pub fn parse_key_constraint_options(&mut self) -> Result<Option<Expression>> {
48233        if self.match_text_seq(&["NO", "ACTION"]) {
48234            // Matched: NO ACTION
48235            return Ok(None);
48236        }
48237        if self.match_text_seq(&["CASCADE"]) {
48238            // Matched: CASCADE
48239            return Ok(None);
48240        }
48241        if self.match_text_seq(&["RESTRICT"]) {
48242            // Matched: RESTRICT
48243            return Ok(None);
48244        }
48245        Ok(None)
48246    }
48247
48248    /// parse_lambda - Ported from Python _parse_lambda
48249    /// Parses lambda expressions: x -> x + 1 or (x, y) -> x + y
48250    /// Also supports DuckDB syntax: LAMBDA x : x + 1
48251    #[allow(unused_variables, unused_mut)]
48252    pub fn parse_lambda(&mut self) -> Result<Option<Expression>> {
48253        let start_index = self.current;
48254
48255        // Check for DuckDB's LAMBDA keyword syntax: LAMBDA x : expr
48256        // ClickHouse doesn't use LAMBDA keyword — lambda is just a function name there
48257        if !matches!(
48258            self.config.dialect,
48259            Some(crate::dialects::DialectType::ClickHouse)
48260        ) && self.match_token(TokenType::Lambda)
48261        {
48262            // Parse lambda parameters (comma-separated identifiers)
48263            let mut params = Vec::new();
48264            loop {
48265                // Use is_identifier_token which handles Identifier, QuotedIdentifier, and Var
48266                if self.is_identifier_token() {
48267                    let token = self.advance();
48268                    let quoted = token.token_type == TokenType::QuotedIdentifier;
48269                    params.push(Identifier {
48270                        name: token.text,
48271                        quoted,
48272                        trailing_comments: Vec::new(),
48273                        span: None,
48274                    });
48275                } else {
48276                    break;
48277                }
48278                if !self.match_token(TokenType::Comma) {
48279                    break;
48280                }
48281            }
48282
48283            // Must have at least one parameter
48284            if params.is_empty() {
48285                return Err(self.parse_error("LAMBDA requires at least one parameter"));
48286            }
48287
48288            // Expect colon separator
48289            if !self.match_token(TokenType::Colon) {
48290                return Err(self.parse_error("Expected ':' after LAMBDA parameters"));
48291            }
48292
48293            let body = self.parse_expression()?;
48294            return Ok(Some(Expression::Lambda(Box::new(LambdaExpr {
48295                parameters: params,
48296                body,
48297                colon: true,
48298                parameter_types: Vec::new(),
48299            }))));
48300        }
48301
48302        // Try to parse lambda parameters
48303        let parameters = if self.match_token(TokenType::LParen) {
48304            // Parenthesized parameters: (x, y) -> ...
48305            let mut params = Vec::new();
48306            if !self.check(TokenType::RParen) {
48307                loop {
48308                    if let Some(ident) = self.parse_identifier()? {
48309                        if let Expression::Identifier(id) = ident {
48310                            params.push(id);
48311                        }
48312                    }
48313                    if !self.match_token(TokenType::Comma) {
48314                        break;
48315                    }
48316                }
48317            }
48318            if !self.match_token(TokenType::RParen) {
48319                // Not a lambda, retreat
48320                self.current = start_index;
48321                return Ok(None);
48322            }
48323            params
48324        } else {
48325            // Single parameter: x -> ...
48326            if let Some(ident) = self.parse_identifier()? {
48327                if let Expression::Identifier(id) = ident {
48328                    vec![id]
48329                } else {
48330                    self.current = start_index;
48331                    return Ok(None);
48332                }
48333            } else {
48334                return Ok(None);
48335            }
48336        };
48337
48338        // Check for arrow operator
48339        if self.match_token(TokenType::Arrow) || self.match_token(TokenType::FArrow) {
48340            // Parse lambda body
48341            let body = self.parse_expression()?;
48342            Ok(Some(Expression::Lambda(Box::new(LambdaExpr {
48343                parameters,
48344                body,
48345                colon: false,
48346                parameter_types: Vec::new(),
48347            }))))
48348        } else {
48349            // Not a lambda, retreat
48350            self.current = start_index;
48351            Ok(None)
48352        }
48353    }
48354
48355    /// parse_lambda_arg - Delegates to parse_id_var
48356    #[allow(unused_variables, unused_mut)]
48357    pub fn parse_lambda_arg(&mut self) -> Result<Option<Expression>> {
48358        self.parse_id_var()
48359    }
48360
48361    /// parse_lateral - Parse LATERAL subquery or table function
48362    /// Python: if self._match(TokenType.LATERAL): return exp.Lateral(this=..., view=..., outer=...)
48363    pub fn parse_lateral(&mut self) -> Result<Option<Expression>> {
48364        // Check for CROSS APPLY / OUTER APPLY (handled by join parsing in try_parse_join_kind)
48365        // This method focuses on LATERAL keyword parsing
48366
48367        if !self.match_token(TokenType::Lateral) {
48368            return Ok(None);
48369        }
48370
48371        // Check for LATERAL VIEW (Hive/Spark syntax)
48372        let view = self.match_token(TokenType::View);
48373        let outer = if view {
48374            self.match_token(TokenType::Outer)
48375        } else {
48376            false
48377        };
48378
48379        // Parse the lateral expression (subquery, function call, or table reference)
48380        let this = if self.check(TokenType::LParen) {
48381            // Could be a subquery: LATERAL (SELECT ...)
48382            self.expect(TokenType::LParen)?;
48383            let inner = self.parse_statement()?;
48384            self.expect(TokenType::RParen)?;
48385            inner
48386        } else {
48387            // Could be a function or table reference: LATERAL unnest(...)
48388            self.parse_primary()?
48389        };
48390
48391        // Parse optional alias
48392        let alias = if self.match_token(TokenType::As) {
48393            Some(self.expect_identifier()?)
48394        } else if self.check(TokenType::Identifier) && !self.check_keyword() {
48395            Some(self.expect_identifier()?)
48396        } else {
48397            None
48398        };
48399
48400        // Parse optional column aliases: AS alias(col1, col2, ...)
48401        let column_aliases = if alias.is_some() && self.match_token(TokenType::LParen) {
48402            let mut cols = Vec::new();
48403            loop {
48404                if self.check(TokenType::RParen) {
48405                    break;
48406                }
48407                let col = self.expect_identifier()?;
48408                cols.push(col);
48409                if !self.match_token(TokenType::Comma) {
48410                    break;
48411                }
48412            }
48413            self.expect(TokenType::RParen)?;
48414            cols
48415        } else {
48416            Vec::new()
48417        };
48418
48419        Ok(Some(Expression::Lateral(Box::new(Lateral {
48420            this: Box::new(this),
48421            view: if view {
48422                Some(Box::new(Expression::Boolean(BooleanLiteral {
48423                    value: true,
48424                })))
48425            } else {
48426                None
48427            },
48428            outer: if outer {
48429                Some(Box::new(Expression::Boolean(BooleanLiteral {
48430                    value: true,
48431                })))
48432            } else {
48433                None
48434            },
48435            alias,
48436            alias_quoted: false,
48437            cross_apply: None,
48438            ordinality: None,
48439            column_aliases,
48440        }))))
48441    }
48442
48443    /// parse_limit - Parse LIMIT clause
48444    /// Python: if self._match(TokenType.LIMIT): return exp.Limit(this=self._parse_term())
48445    pub fn parse_limit(&mut self) -> Result<Option<Expression>> {
48446        if !self.match_token(TokenType::Limit) {
48447            return Ok(None);
48448        }
48449        // Parse the limit expression (usually a number)
48450        let limit_expr = self.parse_expression()?;
48451        Ok(Some(Expression::Limit(Box::new(Limit {
48452            this: limit_expr,
48453            percent: false,
48454            comments: Vec::new(),
48455        }))))
48456    }
48457
48458    /// parse_limit_by - Implemented from Python _parse_limit_by
48459    #[allow(unused_variables, unused_mut)]
48460    pub fn parse_limit_by(&mut self) -> Result<Option<Expression>> {
48461        if self.match_text_seq(&["BY"]) {
48462            // Matched: BY
48463            return Ok(None);
48464        }
48465        Ok(None)
48466    }
48467
48468    /// parse_limit_options - Implemented from Python _parse_limit_options
48469    #[allow(unused_variables, unused_mut)]
48470    pub fn parse_limit_options(&mut self) -> Result<Option<Expression>> {
48471        if self.match_text_seq(&["ONLY"]) {
48472            return Ok(Some(Expression::LimitOptions(Box::new(LimitOptions {
48473                percent: None,
48474                rows: None,
48475                with_ties: None,
48476            }))));
48477        }
48478        if self.match_text_seq(&["WITH", "TIES"]) {
48479            // Matched: WITH TIES
48480            return Ok(None);
48481        }
48482        Ok(None)
48483    }
48484
48485    /// parse_load - Implemented from Python _parse_load
48486    #[allow(unused_variables, unused_mut)]
48487    pub fn parse_load(&mut self) -> Result<Option<Expression>> {
48488        if self.match_text_seq(&["DATA"]) {
48489            return Ok(Some(Expression::Command(Box::new(Command {
48490                this: String::new(),
48491            }))));
48492        }
48493        if self.match_text_seq(&["LOCAL"]) {
48494            // Matched: LOCAL
48495            return Ok(None);
48496        }
48497        Ok(None)
48498    }
48499
48500    /// parse_locking - Implemented from Python _parse_locking
48501    /// Calls: parse_table_parts
48502    #[allow(unused_variables, unused_mut)]
48503    pub fn parse_locking(&mut self) -> Result<Option<Expression>> {
48504        let kind = if self.match_token(TokenType::Table) {
48505            Some("TABLE")
48506        } else if self.match_token(TokenType::View) {
48507            Some("VIEW")
48508        } else if self.match_token(TokenType::Row) {
48509            Some("ROW")
48510        } else if self.match_token(TokenType::Database) || self.match_identifier("DATABASE") {
48511            Some("DATABASE")
48512        } else {
48513            None
48514        };
48515
48516        let kind = match kind {
48517            Some(k) => k.to_string(),
48518            None => return Ok(None),
48519        };
48520
48521        let this = if matches!(kind.as_str(), "DATABASE" | "TABLE" | "VIEW") {
48522            self.parse_table_parts()?
48523        } else {
48524            None
48525        };
48526
48527        let for_or_in = if self.match_token(TokenType::For) {
48528            Some("FOR")
48529        } else if self.match_token(TokenType::In) {
48530            Some("IN")
48531        } else {
48532            None
48533        };
48534
48535        let lock_type = if self.match_identifier("ACCESS") {
48536            Some("ACCESS")
48537        } else if self.match_texts(&["EXCL", "EXCLUSIVE"]) {
48538            Some("EXCLUSIVE")
48539        } else if self.match_identifier("SHARE") {
48540            Some("SHARE")
48541        } else if self.match_identifier("READ") {
48542            Some("READ")
48543        } else if self.match_identifier("WRITE") {
48544            Some("WRITE")
48545        } else if self.match_identifier("CHECKSUM") {
48546            Some("CHECKSUM")
48547        } else {
48548            None
48549        };
48550
48551        let override_ = if self.match_identifier("OVERRIDE") {
48552            Some(Box::new(Expression::Boolean(BooleanLiteral {
48553                value: true,
48554            })))
48555        } else {
48556            None
48557        };
48558
48559        Ok(Some(Expression::LockingProperty(Box::new(
48560            LockingProperty {
48561                this: this.map(Box::new),
48562                kind,
48563                for_or_in: for_or_in.map(|v| {
48564                    Box::new(Expression::Var(Box::new(Var {
48565                        this: v.to_string(),
48566                    })))
48567                }),
48568                lock_type: lock_type.map(|v| {
48569                    Box::new(Expression::Var(Box::new(Var {
48570                        this: v.to_string(),
48571                    })))
48572                }),
48573                override_,
48574            },
48575        ))))
48576    }
48577
48578    /// Parse Teradata LOCKING statement: LOCKING <property> SELECT ...
48579    fn parse_locking_statement(&mut self) -> Result<Expression> {
48580        self.expect(TokenType::Lock)?;
48581        let locking = self
48582            .parse_locking()?
48583            .ok_or_else(|| self.parse_error("Expected LOCKING clause"))?;
48584        let query = if self.check(TokenType::With) {
48585            self.parse_statement()?
48586        } else {
48587            self.parse_select()?
48588        };
48589        Ok(Expression::LockingStatement(Box::new(LockingStatement {
48590            this: Box::new(locking),
48591            expression: Box::new(query),
48592        })))
48593    }
48594
48595    /// parse_log - Parses LOG property (Teradata)
48596    /// Python: _parse_log
48597    /// Creates a LogProperty expression
48598    pub fn parse_log(&mut self) -> Result<Option<Expression>> {
48599        self.parse_log_impl(false)
48600    }
48601
48602    /// Implementation of parse_log with no flag
48603    pub fn parse_log_impl(&mut self, no: bool) -> Result<Option<Expression>> {
48604        Ok(Some(Expression::LogProperty(Box::new(LogProperty {
48605            no: if no {
48606                Some(Box::new(Expression::Boolean(BooleanLiteral {
48607                    value: true,
48608                })))
48609            } else {
48610                None
48611            },
48612        }))))
48613    }
48614
48615    /// parse_match_against - Parses MATCH(columns) AGAINST(pattern)
48616    /// Python: parser.py:7125-7153
48617    #[allow(unused_variables, unused_mut)]
48618    pub fn parse_match_against(&mut self) -> Result<Option<Expression>> {
48619        // Parse column expressions or TABLE syntax
48620        let expressions = if self.match_text_seq(&["TABLE"]) {
48621            // SingleStore TABLE syntax
48622            if let Some(table) = self.parse_table()? {
48623                vec![table]
48624            } else {
48625                Vec::new()
48626            }
48627        } else {
48628            // Regular column list
48629            let mut cols = Vec::new();
48630            loop {
48631                if let Some(col) = self.parse_column()? {
48632                    cols.push(col);
48633                }
48634                if !self.match_token(TokenType::Comma) {
48635                    break;
48636                }
48637            }
48638            cols
48639        };
48640
48641        // Match ) AGAINST (
48642        self.match_text_seq(&[")", "AGAINST", "("]);
48643
48644        // Parse the search pattern
48645        let this = self.parse_string()?;
48646
48647        // Parse modifier
48648        let modifier = if self.match_text_seq(&["IN", "NATURAL", "LANGUAGE", "MODE"]) {
48649            if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
48650                Some(Box::new(Expression::Var(Box::new(Var {
48651                    this: "IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION".to_string(),
48652                }))))
48653            } else {
48654                Some(Box::new(Expression::Var(Box::new(Var {
48655                    this: "IN NATURAL LANGUAGE MODE".to_string(),
48656                }))))
48657            }
48658        } else if self.match_text_seq(&["IN", "BOOLEAN", "MODE"]) {
48659            Some(Box::new(Expression::Var(Box::new(Var {
48660                this: "IN BOOLEAN MODE".to_string(),
48661            }))))
48662        } else if self.match_text_seq(&["WITH", "QUERY", "EXPANSION"]) {
48663            Some(Box::new(Expression::Var(Box::new(Var {
48664                this: "WITH QUERY EXPANSION".to_string(),
48665            }))))
48666        } else {
48667            None
48668        };
48669
48670        match this {
48671            Some(t) => Ok(Some(Expression::MatchAgainst(Box::new(MatchAgainst {
48672                this: Box::new(t),
48673                expressions,
48674                modifier,
48675            })))),
48676            None => Ok(None),
48677        }
48678    }
48679
48680    /// parse_match_recognize_measure - Implemented from Python _parse_match_recognize_measure
48681    /// Parses a MEASURES expression in MATCH_RECOGNIZE: [FINAL|RUNNING] expression
48682    pub fn parse_match_recognize_measure(&mut self) -> Result<Option<Expression>> {
48683        // Check for optional FINAL or RUNNING keyword
48684        let window_frame = if self.match_texts(&["FINAL", "RUNNING"]) {
48685            let text = self.previous().text.to_ascii_uppercase();
48686            Some(if text == "FINAL" {
48687                MatchRecognizeSemantics::Final
48688            } else {
48689                MatchRecognizeSemantics::Running
48690            })
48691        } else {
48692            None
48693        };
48694
48695        // Parse the expression
48696        let this = self.parse_expression()?;
48697
48698        Ok(Some(Expression::MatchRecognizeMeasure(Box::new(
48699            MatchRecognizeMeasure { this, window_frame },
48700        ))))
48701    }
48702
48703    /// parse_max_min_by - MAX_BY / MIN_BY / ARG_MAX / ARG_MIN aggregate functions
48704    /// Parses: MAX_BY(value, key [, n]) or MIN_BY(value, key [, n])
48705    /// is_max: true for MAX_BY/ARG_MAX, false for MIN_BY/ARG_MIN
48706    #[allow(unused_variables, unused_mut)]
48707    pub fn parse_max_min_by(&mut self, is_max: bool) -> Result<Option<Expression>> {
48708        let mut args = Vec::new();
48709
48710        // Handle optional DISTINCT
48711        let distinct = if self.match_token(TokenType::Distinct) {
48712            let lambda_expr = self.parse_lambda()?;
48713            if let Some(expr) = lambda_expr {
48714                args.push(expr);
48715            }
48716            self.match_token(TokenType::Comma);
48717            true
48718        } else {
48719            false
48720        };
48721
48722        // Parse remaining arguments
48723        loop {
48724            if let Some(arg) = self.parse_lambda()? {
48725                args.push(arg);
48726            } else {
48727                break;
48728            }
48729            if !self.match_token(TokenType::Comma) {
48730                break;
48731            }
48732        }
48733
48734        let this = args
48735            .get(0)
48736            .cloned()
48737            .map(Box::new)
48738            .unwrap_or_else(|| Box::new(Expression::Null(Null)));
48739        let expression = args
48740            .get(1)
48741            .cloned()
48742            .map(Box::new)
48743            .unwrap_or_else(|| Box::new(Expression::Null(Null)));
48744        let count = args.get(2).cloned().map(Box::new);
48745
48746        if is_max {
48747            Ok(Some(Expression::ArgMax(Box::new(ArgMax {
48748                this,
48749                expression,
48750                count,
48751            }))))
48752        } else {
48753            Ok(Some(Expression::ArgMin(Box::new(ArgMin {
48754                this,
48755                expression,
48756                count,
48757            }))))
48758        }
48759    }
48760
48761    /// Parse MERGE statement
48762    /// Python: def _parse_merge(self) -> exp.Merge
48763    pub fn parse_merge(&mut self) -> Result<Option<Expression>> {
48764        // Optional INTO keyword
48765        self.match_token(TokenType::Into);
48766
48767        // Parse target table using parse_table_ref
48768        let mut target = Expression::Table(Box::new(self.parse_table_ref()?));
48769
48770        // Parse optional TSQL table hints: WITH (HOLDLOCK), WITH (TABLOCK), etc.
48771        if self.check(TokenType::With) && self.check_next(TokenType::LParen) {
48772            if let Expression::Table(ref mut table) = target {
48773                if let Some(hint_expr) = self.parse_table_hints()? {
48774                    match hint_expr {
48775                        Expression::Tuple(tuple) => {
48776                            table.hints = tuple.expressions;
48777                        }
48778                        other => {
48779                            table.hints = vec![other];
48780                        }
48781                    }
48782                }
48783            }
48784        }
48785
48786        // Parse optional alias for target table
48787        // Try to get an identifier as alias if AS is present or there's an identifier
48788        // Use parse_id_var instead of parse_identifier to handle Var tokens (e.g. T)
48789        if self.match_token(TokenType::As) {
48790            if let Some(alias_expr) = self.parse_id_var()? {
48791                // Extract identifier from the expression
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        } else if !self.check(TokenType::Using) {
48804            // Try to parse alias without AS keyword (e.g., MERGE t1 T USING ...)
48805            // Use parse_id_var to handle both Identifier and Var tokens
48806            if let Some(alias_expr) = self.parse_id_var()? {
48807                if let Expression::Identifier(ident) = alias_expr {
48808                    target = Expression::Alias(Box::new(Alias {
48809                        this: target,
48810                        alias: ident,
48811                        column_aliases: Vec::new(),
48812                        pre_alias_comments: Vec::new(),
48813                        trailing_comments: Vec::new(),
48814                        inferred_type: None,
48815                    }));
48816                }
48817            }
48818        }
48819
48820        // USING clause
48821        if !self.match_token(TokenType::Using) {
48822            return Err(self.parse_error("Expected USING in MERGE statement"));
48823        }
48824
48825        // Parse source table or subquery
48826        let mut using = if self.match_token(TokenType::LParen) {
48827            // Subquery: USING (SELECT ...) AS alias
48828            let query = self.parse_statement()?;
48829            self.expect(TokenType::RParen)?;
48830            let trailing = self.previous_trailing_comments().to_vec();
48831            let mut subq = Subquery {
48832                this: query,
48833                alias: None,
48834                column_aliases: Vec::new(),
48835                order_by: None,
48836                limit: None,
48837                offset: None,
48838                distribute_by: None,
48839                sort_by: None,
48840                cluster_by: None,
48841                lateral: false,
48842                modifiers_inside: false,
48843                trailing_comments: trailing,
48844                inferred_type: None,
48845            };
48846            // Parse optional alias: (SELECT ...) AS y(col1, col2)
48847            if self.match_token(TokenType::As) {
48848                let alias_name = self.expect_identifier_or_keyword()?;
48849                subq.alias = Some(Identifier::new(alias_name));
48850                // Parse optional column aliases: AS alias(col1, col2)
48851                if self.match_token(TokenType::LParen) {
48852                    let mut cols = Vec::new();
48853                    loop {
48854                        let col_name = self.expect_identifier_or_keyword()?;
48855                        cols.push(Identifier::new(col_name));
48856                        if !self.match_token(TokenType::Comma) {
48857                            break;
48858                        }
48859                    }
48860                    self.expect(TokenType::RParen)?;
48861                    subq.column_aliases = cols;
48862                }
48863            } else if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
48864                // Implicit alias without AS
48865                let alias_name = self.expect_identifier_or_keyword()?;
48866                subq.alias = Some(Identifier::new(alias_name));
48867                // Parse optional column aliases: alias(col1, col2)
48868                if self.match_token(TokenType::LParen) {
48869                    let mut cols = Vec::new();
48870                    loop {
48871                        let col_name = self.expect_identifier_or_keyword()?;
48872                        cols.push(Identifier::new(col_name));
48873                        if !self.match_token(TokenType::Comma) {
48874                            break;
48875                        }
48876                    }
48877                    self.expect(TokenType::RParen)?;
48878                    subq.column_aliases = cols;
48879                }
48880            }
48881            Expression::Subquery(Box::new(subq))
48882        } else {
48883            Expression::Table(Box::new(self.parse_table_ref()?))
48884        };
48885
48886        // Parse optional alias for source (if not already parsed for subquery)
48887        if matches!(&using, Expression::Table(_)) {
48888            if self.match_token(TokenType::As) {
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            } else if !self.check(TokenType::On) {
48902                // Try to parse alias without AS keyword
48903                // Use parse_id_var to handle both Identifier and Var tokens (e.g., S, T)
48904                if let Some(alias_expr) = self.parse_id_var()? {
48905                    if let Expression::Identifier(ident) = alias_expr {
48906                        using = Expression::Alias(Box::new(Alias {
48907                            this: using,
48908                            alias: ident,
48909                            column_aliases: Vec::new(),
48910                            pre_alias_comments: Vec::new(),
48911                            trailing_comments: Vec::new(),
48912                            inferred_type: None,
48913                        }));
48914                    }
48915                }
48916            }
48917        }
48918
48919        // ON clause with condition
48920        let on = if self.match_token(TokenType::On) {
48921            Some(Box::new(self.parse_expression()?))
48922        } else {
48923            None
48924        };
48925
48926        // Optional additional USING clause for key columns (DuckDB: USING (col1, col2))
48927        let using_cond = if self.match_token(TokenType::Using) {
48928            // Parse comma-separated identifiers wrapped in parentheses
48929            if self.match_token(TokenType::LParen) {
48930                let mut idents = Vec::new();
48931                loop {
48932                    // Use parse_id_var to handle Var tokens (unquoted identifiers)
48933                    if let Some(ident) = self.parse_id_var()? {
48934                        idents.push(ident);
48935                    } else {
48936                        break;
48937                    }
48938                    if !self.match_token(TokenType::Comma) {
48939                        break;
48940                    }
48941                }
48942                self.match_token(TokenType::RParen);
48943                if !idents.is_empty() {
48944                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
48945                        expressions: idents,
48946                    }))))
48947                } else {
48948                    None
48949                }
48950            } else {
48951                // Also support without parentheses for backwards compatibility
48952                let mut idents = Vec::new();
48953                loop {
48954                    if let Some(ident) = self.parse_id_var()? {
48955                        idents.push(ident);
48956                    } else {
48957                        break;
48958                    }
48959                    if !self.match_token(TokenType::Comma) {
48960                        break;
48961                    }
48962                }
48963                if !idents.is_empty() {
48964                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
48965                        expressions: idents,
48966                    }))))
48967                } else {
48968                    None
48969                }
48970            }
48971        } else {
48972            None
48973        };
48974
48975        // Parse WHEN MATCHED clauses
48976        let whens = self.parse_when_matched_clauses()?;
48977
48978        // Parse optional RETURNING clause (PostgreSQL) or OUTPUT clause (TSQL)
48979        let returning = if let Some(ret) = self.parse_returning()? {
48980            Some(ret)
48981        } else if self.match_token(TokenType::Output) {
48982            // TSQL OUTPUT clause: OUTPUT $action, Inserted.col, Deleted.col [INTO target]
48983            let output = self.parse_output_clause()?;
48984            Some(Expression::Returning(Box::new(Returning {
48985                expressions: output.columns,
48986                into: output.into_table.map(Box::new),
48987            })))
48988        } else {
48989            None
48990        };
48991
48992        Ok(Some(Expression::Merge(Box::new(Merge {
48993            this: Box::new(target),
48994            using: Box::new(using),
48995            on,
48996            using_cond,
48997            whens: whens.map(Box::new),
48998            with_: None,
48999            returning: returning.map(Box::new),
49000        }))))
49001    }
49002
49003    /// Parse multiple WHEN [NOT] MATCHED clauses for MERGE
49004    fn parse_when_matched_clauses(&mut self) -> Result<Option<Expression>> {
49005        let mut whens = Vec::new();
49006
49007        while self.match_token(TokenType::When) {
49008            // Check for NOT MATCHED
49009            let matched = !self.match_token(TokenType::Not);
49010            self.match_text_seq(&["MATCHED"]);
49011
49012            // Check for BY TARGET or BY SOURCE
49013            let source = if self.match_text_seq(&["BY", "TARGET"]) {
49014                Some(Box::new(Expression::Boolean(BooleanLiteral {
49015                    value: false,
49016                })))
49017            } else if self.match_text_seq(&["BY", "SOURCE"]) {
49018                Some(Box::new(Expression::Boolean(BooleanLiteral {
49019                    value: true,
49020                })))
49021            } else {
49022                None
49023            };
49024
49025            // Optional AND condition
49026            let condition = if self.match_token(TokenType::And) {
49027                Some(Box::new(self.parse_expression()?))
49028            } else {
49029                None
49030            };
49031
49032            // THEN action
49033            if !self.match_token(TokenType::Then) {
49034                return Err(self.parse_error("Expected THEN in WHEN clause"));
49035            }
49036
49037            // Parse the action: INSERT, UPDATE, DELETE, or other keywords (DO NOTHING, etc.)
49038            let then: Expression = if self.match_token(TokenType::Insert) {
49039                // INSERT action - use Tuple to represent it
49040                let mut elements = vec![Expression::Var(Box::new(Var {
49041                    this: "INSERT".to_string(),
49042                }))];
49043
49044                // Spark/Databricks: INSERT * (insert all columns)
49045                if self.match_token(TokenType::Star) {
49046                    elements.push(Expression::Star(crate::expressions::Star {
49047                        table: None,
49048                        except: None,
49049                        replace: None,
49050                        rename: None,
49051                        trailing_comments: Vec::new(),
49052                        span: None,
49053                    }));
49054                } else
49055                // Parse column list (optional)
49056                if self.match_token(TokenType::LParen) {
49057                    let mut columns: Vec<Expression> = Vec::new();
49058                    loop {
49059                        if let Some(col) = self.parse_id_var()? {
49060                            // Handle qualified column references (e.g., target.a)
49061                            let col = if self.match_token(TokenType::Dot) {
49062                                if let Expression::Identifier(table_ident) = col {
49063                                    if let Some(col_expr) = self.parse_id_var()? {
49064                                        if let Expression::Identifier(col_ident) = col_expr {
49065                                            Expression::boxed_column(Column {
49066                                                name: col_ident,
49067                                                table: Some(table_ident),
49068                                                join_mark: false,
49069                                                trailing_comments: Vec::new(),
49070                                                span: None,
49071                                                inferred_type: None,
49072                                            })
49073                                        } else {
49074                                            col_expr
49075                                        }
49076                                    } else {
49077                                        return Err(self.parse_error(
49078                                            "Expected column name after dot in MERGE INSERT",
49079                                        ));
49080                                    }
49081                                } else {
49082                                    col
49083                                }
49084                            } else {
49085                                col
49086                            };
49087                            columns.push(col);
49088                        } else {
49089                            break;
49090                        }
49091                        if !self.match_token(TokenType::Comma) {
49092                            break;
49093                        }
49094                    }
49095                    self.match_token(TokenType::RParen);
49096                    if !columns.is_empty() {
49097                        elements.push(Expression::Tuple(Box::new(Tuple {
49098                            expressions: columns,
49099                        })));
49100                    }
49101                }
49102
49103                // Parse VALUES clause
49104                if self.match_text_seq(&["VALUES"]) {
49105                    if let Some(values) = self.parse_value()? {
49106                        elements.push(values);
49107                    }
49108                } else if self.match_text_seq(&["ROW"]) {
49109                    elements.push(Expression::Var(Box::new(Var {
49110                        this: "ROW".to_string(),
49111                    })));
49112                }
49113
49114                if elements.len() == 1 {
49115                    elements[0].clone()
49116                } else {
49117                    Expression::Tuple(Box::new(Tuple {
49118                        expressions: elements,
49119                    }))
49120                }
49121            } else if self.match_token(TokenType::Update) {
49122                // UPDATE action - use Tuple to represent SET assignments
49123                let mut elements = vec![Expression::Var(Box::new(Var {
49124                    this: "UPDATE".to_string(),
49125                }))];
49126
49127                // Spark/Databricks: UPDATE * (update all columns)
49128                if self.match_token(TokenType::Star) {
49129                    elements.push(Expression::Star(crate::expressions::Star {
49130                        table: None,
49131                        except: None,
49132                        replace: None,
49133                        rename: None,
49134                        trailing_comments: Vec::new(),
49135                        span: None,
49136                    }));
49137                } else if self.match_token(TokenType::Set) {
49138                    // Parse col = value assignments manually
49139                    let mut assignments: Vec<Expression> = Vec::new();
49140                    loop {
49141                        // Parse: column = expression (column can be qualified like x.a)
49142                        if let Some(col) = self.parse_id_var()? {
49143                            // Handle qualified column references (e.g., x.a = y.b)
49144                            let col = if self.match_token(TokenType::Dot) {
49145                                // We have a qualified column reference
49146                                if let Expression::Identifier(table_ident) = col {
49147                                    // Parse the column part after the dot
49148                                    if let Some(col_expr) = self.parse_id_var()? {
49149                                        if let Expression::Identifier(col_ident) = col_expr {
49150                                            Expression::boxed_column(Column {
49151                                                name: col_ident,
49152                                                table: Some(table_ident),
49153                                                join_mark: false,
49154                                                trailing_comments: Vec::new(),
49155                                                span: None,
49156                                                inferred_type: None,
49157                                            })
49158                                        } else {
49159                                            col_expr
49160                                        }
49161                                    } else {
49162                                        return Err(
49163                                            self.parse_error("Expected column name after dot")
49164                                        );
49165                                    }
49166                                } else {
49167                                    col
49168                                }
49169                            } else {
49170                                col
49171                            };
49172                            if self.match_token(TokenType::Eq) {
49173                                let value = self.parse_expression()?;
49174                                // Create assignment as EQ expression
49175                                let assignment = Expression::Eq(Box::new(BinaryOp {
49176                                    left: col,
49177                                    right: value,
49178                                    left_comments: Vec::new(),
49179                                    operator_comments: Vec::new(),
49180                                    trailing_comments: Vec::new(),
49181                                    inferred_type: None,
49182                                }));
49183                                assignments.push(assignment);
49184                            }
49185                        }
49186                        if !self.match_token(TokenType::Comma) {
49187                            break;
49188                        }
49189                    }
49190                    if !assignments.is_empty() {
49191                        elements.push(Expression::Tuple(Box::new(Tuple {
49192                            expressions: assignments,
49193                        })));
49194                    }
49195                }
49196
49197                if elements.len() == 1 {
49198                    elements[0].clone()
49199                } else {
49200                    Expression::Tuple(Box::new(Tuple {
49201                        expressions: elements,
49202                    }))
49203                }
49204            } else if self.match_token(TokenType::Delete) {
49205                // DELETE action
49206                Expression::Var(Box::new(Var {
49207                    this: "DELETE".to_string(),
49208                }))
49209            } else if self.match_identifier("DO") {
49210                // DO NOTHING action (PostgreSQL)
49211                if self.match_identifier("NOTHING") {
49212                    Expression::Var(Box::new(Var {
49213                        this: "DO NOTHING".to_string(),
49214                    }))
49215                } else {
49216                    return Err(self.parse_error("Expected NOTHING after DO"));
49217                }
49218            } else {
49219                // Other action
49220                if let Some(var) = self.parse_var()? {
49221                    var
49222                } else {
49223                    return Err(
49224                        self.parse_error("Expected INSERT, UPDATE, DELETE, or action keyword")
49225                    );
49226                }
49227            };
49228
49229            whens.push(Expression::When(Box::new(When {
49230                matched: Some(Box::new(Expression::Boolean(BooleanLiteral {
49231                    value: matched,
49232                }))),
49233                source,
49234                condition,
49235                then: Box::new(then),
49236            })));
49237        }
49238
49239        if whens.is_empty() {
49240            Ok(None)
49241        } else {
49242            Ok(Some(Expression::Whens(Box::new(Whens {
49243                expressions: whens,
49244            }))))
49245        }
49246    }
49247
49248    /// parse_mergeblockratio - Parses MERGEBLOCKRATIO property (Teradata)
49249    /// Python: _parse_mergeblockratio
49250    /// Format: MERGEBLOCKRATIO = number [PERCENT] or NO MERGEBLOCKRATIO or DEFAULT MERGEBLOCKRATIO
49251    pub fn parse_mergeblockratio(&mut self) -> Result<Option<Expression>> {
49252        self.parse_mergeblockratio_impl(false, false)
49253    }
49254
49255    /// Implementation of parse_mergeblockratio with options
49256    pub fn parse_mergeblockratio_impl(
49257        &mut self,
49258        no: bool,
49259        default: bool,
49260    ) -> Result<Option<Expression>> {
49261        // Check for = followed by a number
49262        if self.match_token(TokenType::Eq) {
49263            let this = self.parse_number()?;
49264            let percent = self.match_token(TokenType::Percent);
49265
49266            Ok(Some(Expression::MergeBlockRatioProperty(Box::new(
49267                MergeBlockRatioProperty {
49268                    this: this.map(Box::new),
49269                    no: None,
49270                    default: None,
49271                    percent: if percent {
49272                        Some(Box::new(Expression::Boolean(BooleanLiteral {
49273                            value: true,
49274                        })))
49275                    } else {
49276                        None
49277                    },
49278                },
49279            ))))
49280        } else {
49281            // NO or DEFAULT variant
49282            Ok(Some(Expression::MergeBlockRatioProperty(Box::new(
49283                MergeBlockRatioProperty {
49284                    this: None,
49285                    no: if no {
49286                        Some(Box::new(Expression::Boolean(BooleanLiteral {
49287                            value: true,
49288                        })))
49289                    } else {
49290                        None
49291                    },
49292                    default: if default {
49293                        Some(Box::new(Expression::Boolean(BooleanLiteral {
49294                            value: true,
49295                        })))
49296                    } else {
49297                        None
49298                    },
49299                    percent: None,
49300                },
49301            ))))
49302        }
49303    }
49304
49305    /// parse_modifies_property - Implemented from Python _parse_modifies_property
49306    #[allow(unused_variables, unused_mut)]
49307    pub fn parse_modifies_property(&mut self) -> Result<Option<Expression>> {
49308        if self.match_text_seq(&["SQL", "DATA"]) {
49309            // Matched: SQL DATA
49310            return Ok(None);
49311        }
49312        Ok(None)
49313    }
49314
49315    /// parse_multitable_inserts - Parses Oracle's multi-table INSERT (INSERT ALL/FIRST)
49316    /// Python: _parse_multitable_inserts
49317    /// Syntax: INSERT ALL|FIRST [WHEN cond THEN] INTO table [(cols)] [VALUES(...)] ... SELECT ...
49318    pub fn parse_multitable_inserts(
49319        &mut self,
49320        leading_comments: Vec<String>,
49321    ) -> Result<Option<Expression>> {
49322        // Get kind from previous token (ALL or FIRST)
49323        let kind = self.previous().text.to_ascii_uppercase();
49324
49325        let mut expressions = Vec::new();
49326
49327        // Helper closure to parse a single conditional insert
49328        // Returns None when no more INTO clauses found
49329        loop {
49330            // Check for WHEN condition
49331            let condition = if self.match_token(TokenType::When) {
49332                let cond = self.parse_or()?;
49333                self.match_token(TokenType::Then);
49334                Some(cond)
49335            } else {
49336                None
49337            };
49338
49339            // Check for ELSE (used in INSERT FIRST ... ELSE INTO ...)
49340            let is_else = self.match_token(TokenType::Else);
49341
49342            // Must have INTO keyword to continue
49343            if !self.match_token(TokenType::Into) {
49344                break;
49345            }
49346
49347            // Parse table with optional schema (using parse_table_parts for proper schema.table parsing)
49348            let table_expr = self.parse_table_parts()?;
49349
49350            // Extract TableRef from the table expression
49351            let table_ref = if let Some(Expression::Table(t)) = table_expr {
49352                *t
49353            } else {
49354                // Fallback: create empty table ref (shouldn't happen)
49355                TableRef::new("")
49356            };
49357
49358            // Parse optional column list: (col1, col2, ...)
49359            let columns = if self.match_token(TokenType::LParen) {
49360                let cols = self.parse_identifier_list()?;
49361                self.expect(TokenType::RParen)?;
49362                cols
49363            } else {
49364                Vec::new()
49365            };
49366
49367            // Parse optional VALUES clause
49368            let values = if self.match_token(TokenType::Values) {
49369                self.expect(TokenType::LParen)?;
49370                let row = self.parse_expression_list()?;
49371                self.expect(TokenType::RParen)?;
49372                vec![row]
49373            } else {
49374                Vec::new()
49375            };
49376
49377            // Create Insert expression for this INTO clause
49378            let insert_expr = Expression::Insert(Box::new(Insert {
49379                table: table_ref,
49380                columns,
49381                values,
49382                query: None,
49383                overwrite: false,
49384                partition: Vec::new(),
49385                directory: None,
49386                returning: Vec::new(),
49387                output: None,
49388                on_conflict: None,
49389                leading_comments: Vec::new(),
49390                if_exists: false,
49391                with: None,
49392                ignore: false,
49393                source_alias: None,
49394                alias: None,
49395                alias_explicit_as: false,
49396                default_values: false,
49397                by_name: false,
49398                conflict_action: None,
49399                is_replace: false,
49400                replace_where: None,
49401                source: None,
49402                hint: None,
49403                function_target: None,
49404                partition_by: None,
49405                settings: Vec::new(),
49406            }));
49407
49408            // Wrap in ConditionalInsert
49409            let conditional_insert = Expression::ConditionalInsert(Box::new(ConditionalInsert {
49410                this: Box::new(insert_expr),
49411                expression: condition.map(Box::new),
49412                else_: if is_else {
49413                    Some(Box::new(Expression::Boolean(BooleanLiteral {
49414                        value: true,
49415                    })))
49416                } else {
49417                    None
49418                },
49419            }));
49420
49421            expressions.push(conditional_insert);
49422        }
49423
49424        // Parse the source SELECT statement (or subquery)
49425        let source = self.parse_statement()?;
49426
49427        Ok(Some(Expression::MultitableInserts(Box::new(
49428            MultitableInserts {
49429                kind,
49430                expressions,
49431                source: Some(Box::new(source)),
49432                leading_comments,
49433            },
49434        ))))
49435    }
49436
49437    /// parse_name_as_expression - Parse identifier that can be aliased
49438    /// Parses: identifier [AS expression]
49439    #[allow(unused_variables, unused_mut)]
49440    pub fn parse_name_as_expression(&mut self) -> Result<Option<Expression>> {
49441        // Parse the identifier
49442        let this = self.parse_id_var()?;
49443        if this.is_none() {
49444            return Ok(None);
49445        }
49446
49447        // Check for AS alias
49448        if self.match_token(TokenType::Alias) {
49449            let expression = self.parse_disjunction()?;
49450            if expression.is_none() {
49451                return Ok(this);
49452            }
49453
49454            // Extract the identifier for the alias
49455            let alias_ident =
49456                match this.ok_or_else(|| self.parse_error("Expected identifier for alias"))? {
49457                    Expression::Identifier(id) => id,
49458                    _ => Identifier::new(String::new()),
49459                };
49460
49461            return Ok(Some(Expression::Alias(Box::new(Alias {
49462                this: expression.ok_or_else(|| self.parse_error("Expected expression after AS"))?,
49463                alias: alias_ident,
49464                column_aliases: Vec::new(),
49465                pre_alias_comments: Vec::new(),
49466                trailing_comments: Vec::new(),
49467                inferred_type: None,
49468            }))));
49469        }
49470
49471        Ok(this)
49472    }
49473
49474    /// parse_named_window - Ported from Python _parse_named_window
49475    /// Parses a named window definition: name AS (spec)
49476    #[allow(unused_variables, unused_mut)]
49477    pub fn parse_named_window(&mut self) -> Result<Option<Expression>> {
49478        // Parse window name
49479        let name = self.parse_id_var()?;
49480        if name.is_none() {
49481            return Ok(None);
49482        }
49483
49484        // Expect AS
49485        if !self.match_token(TokenType::As) {
49486            return Ok(name); // Just the name, no spec
49487        }
49488
49489        // Parse window spec (parenthesized)
49490        self.expect(TokenType::LParen)?;
49491        let spec = self.parse_window_spec_inner()?;
49492        self.expect(TokenType::RParen)?;
49493
49494        if let (Some(name_expr), Some(spec_expr)) = (name, spec) {
49495            // Create an Alias expression wrapping the spec with the name
49496            let alias_ident = if let Expression::Identifier(id) = name_expr {
49497                id
49498            } else {
49499                Identifier::new("window")
49500            };
49501            Ok(Some(Expression::Alias(Box::new(Alias {
49502                this: spec_expr,
49503                alias: alias_ident,
49504                column_aliases: Vec::new(),
49505                pre_alias_comments: Vec::new(),
49506                trailing_comments: Vec::new(),
49507                inferred_type: None,
49508            }))))
49509        } else {
49510            Ok(None)
49511        }
49512    }
49513
49514    /// parse_next_value_for - Parses NEXT VALUE FOR sequence_name
49515    /// Python: parser.py:6752-6761
49516    #[allow(unused_variables, unused_mut)]
49517    pub fn parse_next_value_for(&mut self) -> Result<Option<Expression>> {
49518        if !self.match_text_seq(&["VALUE", "FOR"]) {
49519            // Retreat if we consumed a token
49520            if self.current > 0 {
49521                self.current -= 1;
49522            }
49523            return Ok(None);
49524        }
49525
49526        // Parse the sequence name as a dotted identifier (db.schema.sequence_name)
49527        // Manually parse identifier parts separated by dots
49528        let first = self
49529            .parse_id_var()?
49530            .ok_or_else(|| self.parse_error("Expected sequence name after NEXT VALUE FOR"))?;
49531        let first_id = match first {
49532            Expression::Identifier(id) => id,
49533            Expression::Var(v) => Identifier {
49534                name: v.this,
49535                quoted: false,
49536                trailing_comments: Vec::new(),
49537                span: None,
49538            },
49539            _ => Identifier {
49540                name: String::new(),
49541                quoted: false,
49542                trailing_comments: Vec::new(),
49543                span: None,
49544            },
49545        };
49546
49547        // Check for dotted parts (db.schema.sequence_name)
49548        let mut parts = vec![first_id];
49549        while self.match_token(TokenType::Dot) {
49550            if self.is_identifier_or_keyword_token() {
49551                let token = self.advance();
49552                parts.push(Identifier {
49553                    name: token.text,
49554                    quoted: token.token_type == TokenType::QuotedIdentifier,
49555                    trailing_comments: Vec::new(),
49556                    span: None,
49557                });
49558            } else {
49559                break;
49560            }
49561        }
49562
49563        // Build a Column expression from the parts
49564        let this = if parts.len() == 1 {
49565            Expression::boxed_column(Column {
49566                name: parts.remove(0),
49567                table: None,
49568                join_mark: false,
49569                trailing_comments: Vec::new(),
49570                span: None,
49571                inferred_type: None,
49572            })
49573        } else if parts.len() == 2 {
49574            Expression::boxed_column(Column {
49575                name: parts.remove(1),
49576                table: Some(parts.remove(0)),
49577                join_mark: false,
49578                trailing_comments: Vec::new(),
49579                span: None,
49580                inferred_type: None,
49581            })
49582        } else {
49583            // For 3+ parts, build nested Dot expressions
49584            let mut expr = Expression::Identifier(parts.remove(0));
49585            for part in parts.drain(..) {
49586                expr = Expression::Dot(Box::new(DotAccess {
49587                    this: expr,
49588                    field: part,
49589                }));
49590            }
49591            expr
49592        };
49593
49594        // Parse optional OVER (ORDER BY ...) clause
49595        let order = if self.match_token(TokenType::Over) {
49596            if self.match_token(TokenType::LParen) {
49597                let ord = self.parse_order()?;
49598                self.expect(TokenType::RParen)?;
49599                ord.map(Box::new)
49600            } else {
49601                None
49602            }
49603        } else {
49604            None
49605        };
49606
49607        Ok(Some(Expression::NextValueFor(Box::new(NextValueFor {
49608            this: Box::new(this),
49609            order,
49610        }))))
49611    }
49612
49613    /// parse_no_property - Implemented from Python _parse_no_property
49614    #[allow(unused_variables, unused_mut)]
49615    pub fn parse_no_property(&mut self) -> Result<Option<Expression>> {
49616        if self.match_text_seq(&["PRIMARY", "INDEX"]) {
49617            // Matched: PRIMARY INDEX
49618            return Ok(None);
49619        }
49620        if self.match_text_seq(&["SQL"]) {
49621            // Matched: SQL
49622            return Ok(None);
49623        }
49624        Ok(None)
49625    }
49626
49627    /// parse_normalize - Ported from Python _parse_normalize
49628    #[allow(unused_variables, unused_mut)]
49629    /// parse_normalize - Parses NORMALIZE(expr [, form])
49630    /// Python: NORMALIZE(expr, form) where form is NFC/NFD/NFKC/NFKD
49631    pub fn parse_normalize(&mut self) -> Result<Option<Expression>> {
49632        // Parse the expression to normalize
49633        let this = self.parse_expression()?;
49634
49635        // Check for optional form argument
49636        let form = if self.match_token(TokenType::Comma) {
49637            self.parse_var()?.map(Box::new)
49638        } else {
49639            None
49640        };
49641
49642        Ok(Some(Expression::Normalize(Box::new(Normalize {
49643            this: Box::new(this),
49644            form,
49645            is_casefold: None,
49646        }))))
49647    }
49648
49649    /// parse_not_constraint - Implemented from Python _parse_not_constraint
49650    /// Parses constraints that start with NOT: NOT NULL, NOT CASESPECIFIC
49651    pub fn parse_not_constraint(&mut self) -> Result<Option<Expression>> {
49652        // NOT NULL constraint
49653        if self.match_text_seq(&["NULL"]) {
49654            return Ok(Some(Expression::NotNullColumnConstraint(Box::new(
49655                NotNullColumnConstraint { allow_null: None },
49656            ))));
49657        }
49658        // NOT CASESPECIFIC constraint (Teradata)
49659        if self.match_text_seq(&["CASESPECIFIC"]) {
49660            return Ok(Some(Expression::CaseSpecificColumnConstraint(Box::new(
49661                CaseSpecificColumnConstraint {
49662                    not_: Some(Box::new(Expression::Boolean(BooleanLiteral {
49663                        value: true,
49664                    }))),
49665                },
49666            ))));
49667        }
49668        // NOT FOR REPLICATION (SQL Server) - consume the tokens and return as a property
49669        if self.match_token(TokenType::For) && self.match_identifier("REPLICATION") {
49670            return Ok(Some(Expression::Property(Box::new(
49671                crate::expressions::Property {
49672                    this: Box::new(Expression::Identifier(Identifier::new(
49673                        "NOT FOR REPLICATION".to_string(),
49674                    ))),
49675                    value: None,
49676                },
49677            ))));
49678        }
49679        Ok(None)
49680    }
49681
49682    /// parse_null - Parse NULL literal
49683    /// Python: if self._match_set((TokenType.NULL, TokenType.UNKNOWN)): return exp.Null
49684    pub fn parse_null(&mut self) -> Result<Option<Expression>> {
49685        if self.match_token(TokenType::Null) {
49686            return Ok(Some(Expression::Null(Null)));
49687        }
49688        // UNKNOWN is treated as NULL in some dialects
49689        if self.match_token(TokenType::Unknown) {
49690            return Ok(Some(Expression::Null(Null)));
49691        }
49692        Ok(None)
49693    }
49694
49695    /// parse_number - Parse numeric literal
49696    /// Python: TokenType.NUMBER -> exp.Literal(this=token.text, is_string=False)
49697    /// Handles Hive/Spark numeric suffixes encoded as "number::TYPE" by the tokenizer
49698    pub fn parse_number(&mut self) -> Result<Option<Expression>> {
49699        if self.match_token(TokenType::Number) {
49700            let text = self.previous().text.clone();
49701            // Check for numeric literal suffix encoded as "number::TYPE"
49702            if let Some(sep_pos) = text.find("::") {
49703                let num_part = &text[..sep_pos];
49704                let type_name = &text[sep_pos + 2..];
49705                // Create a TryCast expression: TRY_CAST(number AS TYPE)
49706                let num_expr = Expression::Literal(Box::new(Literal::Number(num_part.to_string())));
49707                let data_type = match type_name {
49708                    "BIGINT" => crate::expressions::DataType::BigInt { length: None },
49709                    "SMALLINT" => crate::expressions::DataType::SmallInt { length: None },
49710                    "TINYINT" => crate::expressions::DataType::TinyInt { length: None },
49711                    "DOUBLE" => crate::expressions::DataType::Double {
49712                        precision: None,
49713                        scale: None,
49714                    },
49715                    "FLOAT" => crate::expressions::DataType::Float {
49716                        precision: None,
49717                        scale: None,
49718                        real_spelling: false,
49719                    },
49720                    "DECIMAL" => crate::expressions::DataType::Decimal {
49721                        precision: None,
49722                        scale: None,
49723                    },
49724                    _ => crate::expressions::DataType::Custom {
49725                        name: type_name.to_string(),
49726                    },
49727                };
49728                return Ok(Some(Expression::TryCast(Box::new(
49729                    crate::expressions::Cast {
49730                        this: num_expr,
49731                        to: data_type,
49732                        trailing_comments: Vec::new(),
49733                        double_colon_syntax: false,
49734                        format: None,
49735                        default: None,
49736                        inferred_type: None,
49737                    },
49738                ))));
49739            }
49740            return Ok(Some(Expression::Literal(Box::new(Literal::Number(text)))));
49741        }
49742        Ok(None)
49743    }
49744
49745    /// parse_odbc_datetime_literal - Ported from Python _parse_odbc_datetime_literal
49746    #[allow(unused_variables, unused_mut)]
49747    /// parse_odbc_datetime_literal - Parses ODBC datetime literals
49748    /// Examples: {d'2023-01-01'}, {t'12:00:00'}, {ts'2023-01-01 12:00:00'}
49749    pub fn parse_odbc_datetime_literal(&mut self) -> Result<Option<Expression>> {
49750        // Match the type indicator (d, t, ts)
49751        if !self.match_token(TokenType::Var) {
49752            return Ok(None);
49753        }
49754        let type_indicator = self.previous().text.to_lowercase();
49755
49756        // Parse the string value
49757        let value = self.parse_string()?;
49758        if value.is_none() {
49759            return Ok(None);
49760        }
49761
49762        // Expect closing brace
49763        self.expect(TokenType::RBrace)?;
49764
49765        // Return appropriate expression based on type
49766        let value = value
49767            .ok_or_else(|| self.parse_error("Expected string value in ODBC datetime literal"))?;
49768        match type_indicator.as_str() {
49769            "d" => Ok(Some(Expression::Date(Box::new(UnaryFunc::new(value))))),
49770            "t" => Ok(Some(Expression::Time(Box::new(UnaryFunc::new(value))))),
49771            "ts" => Ok(Some(Expression::Timestamp(Box::new(TimestampFunc {
49772                this: Some(Box::new(value)),
49773                zone: None,
49774                with_tz: None,
49775                safe: None,
49776            })))),
49777            _ => Ok(Some(value)),
49778        }
49779    }
49780
49781    /// parse_offset - Parse OFFSET clause
49782    /// Python: if self._match(TokenType.OFFSET): return exp.Offset(this=self._parse_term())
49783    pub fn parse_offset(&mut self) -> Result<Option<Expression>> {
49784        if !self.match_token(TokenType::Offset) {
49785            return Ok(None);
49786        }
49787        // Parse the offset expression (usually a number)
49788        let offset_expr = self.parse_expression()?;
49789        Ok(Some(Expression::Offset(Box::new(Offset {
49790            this: offset_expr,
49791            rows: None,
49792        }))))
49793    }
49794
49795    /// parse_on_condition - Ported from Python _parse_on_condition
49796    #[allow(unused_variables, unused_mut)]
49797    /// parse_on_condition - Parses ON EMPTY/ERROR/NULL conditions
49798    /// Example: NULL ON EMPTY, ERROR ON ERROR
49799    pub fn parse_on_condition(&mut self) -> Result<Option<Expression>> {
49800        // Parse ON EMPTY
49801        let empty = if self.match_text_seq(&["NULL", "ON", "EMPTY"]) {
49802            Some(Box::new(Expression::Identifier(Identifier::new(
49803                "NULL".to_string(),
49804            ))))
49805        } else if self.match_text_seq(&["ERROR", "ON", "EMPTY"]) {
49806            Some(Box::new(Expression::Identifier(Identifier::new(
49807                "ERROR".to_string(),
49808            ))))
49809        } else if self.match_text_seq(&["DEFAULT"]) {
49810            let default_val = self.parse_expression()?;
49811            if self.match_text_seq(&["ON", "EMPTY"]) {
49812                Some(Box::new(default_val))
49813            } else {
49814                None
49815            }
49816        } else {
49817            None
49818        };
49819
49820        // Parse ON ERROR
49821        let error = if self.match_text_seq(&["NULL", "ON", "ERROR"]) {
49822            Some(Box::new(Expression::Identifier(Identifier::new(
49823                "NULL".to_string(),
49824            ))))
49825        } else if self.match_text_seq(&["ERROR", "ON", "ERROR"]) {
49826            Some(Box::new(Expression::Identifier(Identifier::new(
49827                "ERROR".to_string(),
49828            ))))
49829        } else if self.match_text_seq(&["DEFAULT"]) {
49830            let default_val = self.parse_expression()?;
49831            if self.match_text_seq(&["ON", "ERROR"]) {
49832                Some(Box::new(default_val))
49833            } else {
49834                None
49835            }
49836        } else {
49837            None
49838        };
49839
49840        // Parse ON NULL
49841        let null = if self.match_text_seq(&["NULL", "ON", "NULL"]) {
49842            Some(Box::new(Expression::Identifier(Identifier::new(
49843                "NULL".to_string(),
49844            ))))
49845        } else {
49846            None
49847        };
49848
49849        if empty.is_none() && error.is_none() && null.is_none() {
49850            return Ok(None);
49851        }
49852
49853        Ok(Some(Expression::OnCondition(Box::new(OnCondition {
49854            empty,
49855            error,
49856            null,
49857        }))))
49858    }
49859
49860    /// parse_on_handling - Implemented from Python _parse_on_handling
49861    /// Calls: parse_bitwise
49862    #[allow(unused_variables, unused_mut)]
49863    pub fn parse_on_handling(&mut self) -> Result<Option<Expression>> {
49864        if self.match_text_seq(&["ON"]) {
49865            // Matched: ON
49866            return Ok(None);
49867        }
49868        if self.match_text_seq(&["ON"]) {
49869            // Matched: ON
49870            return Ok(None);
49871        }
49872        Ok(None)
49873    }
49874
49875    /// parse_on_property - Implemented from Python _parse_on_property
49876    #[allow(unused_variables, unused_mut)]
49877    pub fn parse_on_property(&mut self) -> Result<Option<Expression>> {
49878        if self.match_text_seq(&["COMMIT", "PRESERVE", "ROWS"]) {
49879            return Ok(Some(Expression::OnCommitProperty(Box::new(
49880                OnCommitProperty { delete: None },
49881            ))));
49882        }
49883        if self.match_text_seq(&["COMMIT", "DELETE", "ROWS"]) {
49884            // Matched: COMMIT DELETE ROWS
49885            return Ok(None);
49886        }
49887        Ok(None)
49888    }
49889
49890    /// parse_opclass - Ported from Python _parse_opclass
49891    #[allow(unused_variables, unused_mut)]
49892    /// parse_opclass - Parses PostgreSQL operator class in index expressions
49893    /// Example: column_name text_pattern_ops
49894    pub fn parse_opclass(&mut self) -> Result<Option<Expression>> {
49895        // Parse the expression first
49896        let this = self.parse_expression()?;
49897
49898        // Check for keywords that would indicate this is not an opclass
49899        // (e.g., ASC, DESC, NULLS, etc.)
49900        if self.check(TokenType::Asc)
49901            || self.check(TokenType::Desc)
49902            || self.check(TokenType::Nulls)
49903            || self.check(TokenType::Comma)
49904            || self.check(TokenType::RParen)
49905        {
49906            return Ok(Some(this));
49907        }
49908
49909        // Try to parse an operator class name (table parts)
49910        if let Some(opclass_name) = self.parse_table()? {
49911            return Ok(Some(Expression::Opclass(Box::new(Opclass {
49912                this: Box::new(this),
49913                expression: Box::new(opclass_name),
49914            }))));
49915        }
49916
49917        Ok(Some(this))
49918    }
49919
49920    /// parse_open_json - Parses SQL Server OPENJSON function
49921    /// Example: OPENJSON(json, '$.path') WITH (col1 type '$.path' AS JSON, ...)
49922    pub fn parse_open_json(&mut self) -> Result<Option<Expression>> {
49923        // Parse the JSON expression
49924        let this = self.parse_expression()?;
49925
49926        // Parse optional path
49927        let path = if self.match_token(TokenType::Comma) {
49928            self.parse_string()?.map(Box::new)
49929        } else {
49930            None
49931        };
49932
49933        // Check for closing paren and WITH clause
49934        let expressions = if self.match_token(TokenType::RParen)
49935            && self.match_token(TokenType::With)
49936        {
49937            self.expect(TokenType::LParen)?;
49938            let mut cols = Vec::new();
49939            loop {
49940                // Parse column definition: name type 'path' [AS JSON]
49941                let col_name = self.parse_field()?;
49942                if col_name.is_none() {
49943                    break;
49944                }
49945                let col_type = self.parse_data_type()?;
49946                let col_path = self.parse_string()?.map(Box::new);
49947                let as_json = if self.match_token(TokenType::As) && self.match_identifier("JSON") {
49948                    Some(Box::new(Expression::Boolean(BooleanLiteral {
49949                        value: true,
49950                    })))
49951                } else {
49952                    None
49953                };
49954                cols.push(Expression::OpenJSONColumnDef(Box::new(OpenJSONColumnDef {
49955                    this: Box::new(col_name.ok_or_else(|| {
49956                        self.parse_error("Expected column name in OPENJSON WITH clause")
49957                    })?),
49958                    kind: String::new(), // kept for backwards compat, use data_type instead
49959                    path: col_path,
49960                    as_json,
49961                    data_type: Some(col_type),
49962                })));
49963                if !self.match_token(TokenType::Comma) {
49964                    break;
49965                }
49966            }
49967            self.expect(TokenType::RParen)?;
49968            cols
49969        } else {
49970            Vec::new()
49971        };
49972
49973        Ok(Some(Expression::OpenJSON(Box::new(OpenJSON {
49974            this: Box::new(this),
49975            path,
49976            expressions,
49977        }))))
49978    }
49979
49980    /// parse_operator - Ported from Python _parse_operator
49981    #[allow(unused_variables, unused_mut)]
49982    /// parse_operator - Parses PostgreSQL OPERATOR(op) syntax
49983    /// Example: col1 OPERATOR(~>) col2
49984    pub fn parse_operator(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
49985        let mut result = this;
49986
49987        // Parse OPERATOR(op) expressions
49988        while self.match_token(TokenType::LParen) {
49989            // Collect the operator text between parens
49990            let mut op_text = String::new();
49991            while !self.check(TokenType::RParen) && !self.is_at_end() {
49992                op_text.push_str(&self.peek().text);
49993                self.skip();
49994            }
49995            self.expect(TokenType::RParen)?;
49996
49997            // Parse the right-hand side expression
49998            let rhs = self.parse_expression()?;
49999
50000            result = Some(Expression::Operator(Box::new(Operator {
50001                this: Box::new(result.unwrap_or_else(|| Expression::Null(Null))),
50002                operator: Some(Box::new(Expression::Identifier(Identifier::new(op_text)))),
50003                expression: Box::new(rhs),
50004                comments: Vec::new(),
50005            })));
50006
50007            // Check if there's another OPERATOR keyword
50008            if !self.match_token(TokenType::Operator) {
50009                break;
50010            }
50011        }
50012
50013        Ok(result)
50014    }
50015
50016    /// parse_order - Parse ORDER BY clause
50017    /// Python: if not self._match(TokenType.ORDER_BY): return this; return exp.Order(expressions=self._parse_csv(self._parse_ordered))
50018    pub fn parse_order(&mut self) -> Result<Option<Expression>> {
50019        if !self.match_token(TokenType::Order) {
50020            return Ok(None);
50021        }
50022        // Consume BY if present
50023        self.match_token(TokenType::By);
50024
50025        // Parse comma-separated ordered expressions
50026        let mut expressions = Vec::new();
50027        loop {
50028            if let Some(ordered) = self.parse_ordered_item()? {
50029                expressions.push(ordered);
50030            } else {
50031                break;
50032            }
50033            if !self.match_token(TokenType::Comma) {
50034                break;
50035            }
50036        }
50037
50038        Ok(Some(Expression::OrderBy(Box::new(OrderBy {
50039            expressions,
50040            siblings: false,
50041            comments: Vec::new(),
50042        }))))
50043    }
50044
50045    /// parse_ordered_item - Parse a single ORDER BY item (expr [ASC|DESC] [NULLS FIRST|LAST])
50046    fn parse_ordered_item(&mut self) -> Result<Option<Ordered>> {
50047        // Parse the expression to order by
50048        let expr = match self.parse_expression() {
50049            Ok(e) => e,
50050            Err(_) => return Ok(None),
50051        };
50052
50053        // Check for ASC/DESC
50054        let mut desc = false;
50055        let mut explicit_asc = false;
50056        if self.match_token(TokenType::Asc) {
50057            explicit_asc = true;
50058        } else if self.match_token(TokenType::Desc) {
50059            desc = true;
50060        }
50061
50062        // Check for NULLS FIRST/LAST
50063        let nulls_first = if self.match_text_seq(&["NULLS", "FIRST"]) {
50064            Some(true)
50065        } else if self.match_text_seq(&["NULLS", "LAST"]) {
50066            Some(false)
50067        } else {
50068            None
50069        };
50070
50071        // Parse optional WITH FILL clause (ClickHouse)
50072        let with_fill = if self.match_text_seq(&["WITH", "FILL"]) {
50073            let from_ = if self.match_token(TokenType::From) {
50074                Some(Box::new(self.parse_or()?))
50075            } else {
50076                None
50077            };
50078            let to = if self.match_text_seq(&["TO"]) {
50079                Some(Box::new(self.parse_or()?))
50080            } else {
50081                None
50082            };
50083            let step = if self.match_text_seq(&["STEP"]) {
50084                Some(Box::new(self.parse_or()?))
50085            } else {
50086                None
50087            };
50088            let staleness = if self.match_text_seq(&["STALENESS"]) {
50089                Some(Box::new(self.parse_or()?))
50090            } else {
50091                None
50092            };
50093            let interpolate = if self.match_text_seq(&["INTERPOLATE"]) {
50094                if self.match_token(TokenType::LParen) {
50095                    let exprs = self.parse_expression_list()?;
50096                    self.expect(TokenType::RParen)?;
50097                    if exprs.len() == 1 {
50098                        Some(Box::new(exprs.into_iter().next().unwrap()))
50099                    } else {
50100                        Some(Box::new(Expression::Tuple(Box::new(
50101                            crate::expressions::Tuple { expressions: exprs },
50102                        ))))
50103                    }
50104                } else {
50105                    None
50106                }
50107            } else {
50108                None
50109            };
50110            Some(Box::new(WithFill {
50111                from_,
50112                to,
50113                step,
50114                staleness,
50115                interpolate,
50116            }))
50117        } else {
50118            None
50119        };
50120
50121        Ok(Some(Ordered {
50122            this: expr,
50123            desc,
50124            nulls_first,
50125            explicit_asc,
50126            with_fill,
50127        }))
50128    }
50129
50130    /// parse_ordered - Implemented from Python _parse_ordered (wrapper for parse_ordered_item)
50131    #[allow(unused_variables, unused_mut)]
50132    pub fn parse_ordered(&mut self) -> Result<Option<Expression>> {
50133        if let Some(ordered) = self.parse_ordered_item()? {
50134            return Ok(Some(Expression::Ordered(Box::new(ordered))));
50135        }
50136        if self.match_text_seq(&["NULLS", "FIRST"]) {
50137            return Ok(Some(Expression::WithFill(Box::new(WithFill {
50138                from_: None,
50139                to: None,
50140                step: None,
50141                staleness: None,
50142                interpolate: None,
50143            }))));
50144        }
50145        if self.match_text_seq(&["NULLS", "LAST"]) {
50146            // Matched: NULLS LAST
50147            return Ok(None);
50148        }
50149        if self.match_text_seq(&["WITH", "FILL"]) {
50150            // Matched: WITH FILL
50151            return Ok(None);
50152        }
50153        Ok(None)
50154    }
50155
50156    /// parse_overlay - Ported from Python _parse_overlay
50157    /// Parses OVERLAY function: OVERLAY(string PLACING replacement FROM position [FOR length])
50158    #[allow(unused_variables, unused_mut)]
50159    pub fn parse_overlay(&mut self) -> Result<Option<Expression>> {
50160        // Parse the string to be modified
50161        let this = match self.parse_bitwise() {
50162            Ok(Some(expr)) => expr,
50163            Ok(None) => return Ok(None),
50164            Err(e) => return Err(e),
50165        };
50166
50167        // Parse PLACING replacement (or comma then replacement)
50168        let replacement = if self.match_text_seq(&["PLACING"]) || self.match_token(TokenType::Comma)
50169        {
50170            match self.parse_bitwise() {
50171                Ok(Some(expr)) => expr,
50172                Ok(None) => {
50173                    return Err(self.parse_error("Expected replacement expression in OVERLAY"))
50174                }
50175                Err(e) => return Err(e),
50176            }
50177        } else {
50178            return Err(self.parse_error("Expected PLACING in OVERLAY function"));
50179        };
50180
50181        // Parse FROM position (or comma then position)
50182        let from = if self.match_token(TokenType::From) || self.match_token(TokenType::Comma) {
50183            match self.parse_bitwise() {
50184                Ok(Some(expr)) => expr,
50185                Ok(None) => return Err(self.parse_error("Expected position expression in OVERLAY")),
50186                Err(e) => return Err(e),
50187            }
50188        } else {
50189            return Err(self.parse_error("Expected FROM in OVERLAY function"));
50190        };
50191
50192        // Parse optional FOR length (or comma then length)
50193        let length = if self.match_token(TokenType::For) || self.match_token(TokenType::Comma) {
50194            match self.parse_bitwise() {
50195                Ok(Some(expr)) => Some(expr),
50196                Ok(None) => None,
50197                Err(_) => None,
50198            }
50199        } else {
50200            None
50201        };
50202
50203        Ok(Some(Expression::Overlay(Box::new(OverlayFunc {
50204            this,
50205            replacement,
50206            from,
50207            length,
50208        }))))
50209    }
50210
50211    /// parse_parameter - Parse named parameter (@name or :name)
50212    /// Python: this = self._parse_identifier() or self._parse_primary_or_var(); return exp.Parameter(this=this)
50213    pub fn parse_parameter(&mut self) -> Result<Option<Expression>> {
50214        // Check for parameter token types
50215        if self.match_token(TokenType::Parameter) {
50216            let text = self.previous().text.clone();
50217            return Ok(Some(Expression::Parameter(Box::new(Parameter {
50218                name: Some(text),
50219                index: None,
50220                style: ParameterStyle::Colon,
50221                quoted: false,
50222                string_quoted: false,
50223                expression: None,
50224            }))));
50225        }
50226
50227        // Check for session parameter (@@name)
50228        if self.match_token(TokenType::SessionParameter) {
50229            let text = self.previous().text.clone();
50230            return Ok(Some(Expression::SessionParameter(Box::new(
50231                SessionParameter {
50232                    this: Box::new(Expression::Identifier(Identifier::new(text))),
50233                    kind: None,
50234                },
50235            ))));
50236        }
50237
50238        Ok(None)
50239    }
50240
50241    /// parse_paren - Ported from Python _parse_paren
50242    /// Parses parenthesized expressions: (expr), (select ...), or (a, b, c)
50243    #[allow(unused_variables, unused_mut)]
50244    pub fn parse_paren(&mut self) -> Result<Option<Expression>> {
50245        if !self.match_token(TokenType::LParen) {
50246            return Ok(None);
50247        }
50248
50249        // Check for empty tuple ()
50250        if self.match_token(TokenType::RParen) {
50251            return Ok(Some(Expression::Tuple(Box::new(Tuple {
50252                expressions: Vec::new(),
50253            }))));
50254        }
50255
50256        // Try to parse as subquery first
50257        // ClickHouse also allows (EXPLAIN ...) as subquery
50258        if self.check(TokenType::Select)
50259            || self.check(TokenType::With)
50260            || (matches!(
50261                self.config.dialect,
50262                Some(crate::dialects::DialectType::ClickHouse)
50263            ) && self.check(TokenType::Var)
50264                && self.peek().text.eq_ignore_ascii_case("EXPLAIN"))
50265        {
50266            let query = self.parse_statement()?;
50267            self.expect(TokenType::RParen)?;
50268            return Ok(Some(Expression::Subquery(Box::new(Subquery {
50269                this: query,
50270                alias: None,
50271                column_aliases: Vec::new(),
50272                order_by: None,
50273                limit: None,
50274                offset: None,
50275                lateral: false,
50276                modifiers_inside: true,
50277                trailing_comments: Vec::new(),
50278                distribute_by: None,
50279                sort_by: None,
50280                cluster_by: None,
50281                inferred_type: None,
50282            }))));
50283        }
50284
50285        // Parse comma-separated expressions
50286        let mut expressions = Vec::new();
50287        let mut trailing_comma = false;
50288        loop {
50289            match self.parse_expression() {
50290                Ok(expr) => expressions.push(expr),
50291                Err(_) => break,
50292            }
50293            if !self.match_token(TokenType::Comma) {
50294                break;
50295            }
50296            // ClickHouse: trailing comma makes a single-element tuple, e.g., (1,)
50297            if self.check(TokenType::RParen) {
50298                trailing_comma = true;
50299                break;
50300            }
50301        }
50302
50303        self.expect(TokenType::RParen)?;
50304
50305        // Single expression with trailing comma → tuple, e.g., (1,)
50306        if trailing_comma && expressions.len() == 1 {
50307            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
50308        }
50309
50310        // Single expression - return the unwrapped Paren
50311        if expressions.len() == 1 {
50312            return Ok(Some(Expression::Paren(Box::new(Paren {
50313                this: expressions.remove(0),
50314                trailing_comments: Vec::new(),
50315            }))));
50316        }
50317
50318        // Multiple expressions - return as tuple
50319        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
50320    }
50321
50322    /// parse_partition - Parses PARTITION/SUBPARTITION clause
50323    /// Python: _parse_partition
50324    pub fn parse_partition(&mut self) -> Result<Option<Expression>> {
50325        // PARTITION_KEYWORDS = {"PARTITION", "SUBPARTITION"}
50326        if !self.match_texts(&["PARTITION", "SUBPARTITION"]) {
50327            return Ok(None);
50328        }
50329
50330        let subpartition = self.previous().text.eq_ignore_ascii_case("SUBPARTITION");
50331
50332        // Parse wrapped CSV of disjunction expressions
50333        if !self.match_token(TokenType::LParen) {
50334            // Without parentheses, still return a Partition with empty expressions
50335            return Ok(Some(Expression::Partition(Box::new(Partition {
50336                expressions: Vec::new(),
50337                subpartition,
50338            }))));
50339        }
50340
50341        let mut expressions = Vec::new();
50342        loop {
50343            if let Some(expr) = self.parse_disjunction()? {
50344                expressions.push(expr);
50345            } else {
50346                break;
50347            }
50348
50349            if !self.match_token(TokenType::Comma) {
50350                break;
50351            }
50352        }
50353
50354        self.match_token(TokenType::RParen);
50355
50356        Ok(Some(Expression::Partition(Box::new(Partition {
50357            expressions,
50358            subpartition,
50359        }))))
50360    }
50361
50362    /// parse_partition_and_order - Delegates to parse_partition_by
50363    #[allow(unused_variables, unused_mut)]
50364    pub fn parse_partition_and_order(&mut self) -> Result<Option<Expression>> {
50365        self.parse_partition_by()
50366    }
50367
50368    /// parse_partition_bound_spec - Implemented from Python _parse_partition_bound_spec
50369    /// Calls: parse_bitwise, parse_number
50370    #[allow(unused_variables, unused_mut)]
50371    pub fn parse_partition_bound_spec_legacy(&mut self) -> Result<Option<Expression>> {
50372        if self.match_text_seq(&["MINVALUE"]) {
50373            return Ok(Some(Expression::PartitionBoundSpec(Box::new(
50374                PartitionBoundSpec {
50375                    this: None,
50376                    expression: None,
50377                    from_expressions: None,
50378                    to_expressions: None,
50379                },
50380            ))));
50381        }
50382        if self.match_text_seq(&["MAXVALUE"]) {
50383            // Matched: MAXVALUE
50384            return Ok(None);
50385        }
50386        if self.match_text_seq(&["TO"]) {
50387            // Matched: TO
50388            return Ok(None);
50389        }
50390        Ok(None)
50391    }
50392
50393    /// parse_partition_by - Ported from Python _parse_partition_by
50394    /// Parses PARTITION BY expression list
50395    #[allow(unused_variables, unused_mut)]
50396    pub fn parse_partition_by(&mut self) -> Result<Option<Expression>> {
50397        if !self.match_keywords(&[TokenType::Partition, TokenType::By]) {
50398            return Ok(None);
50399        }
50400        let expressions = self.parse_expression_list()?;
50401        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
50402    }
50403
50404    /// parse_partitioned_by - Parses PARTITIONED BY clause
50405    /// Python: _parse_partitioned_by
50406    pub fn parse_partitioned_by(&mut self) -> Result<Option<Expression>> {
50407        // Optionally match '='
50408        self.match_token(TokenType::Eq);
50409
50410        // Try to parse a schema first
50411        if let Some(schema) = self.parse_schema()? {
50412            return Ok(Some(Expression::PartitionedByProperty(Box::new(
50413                PartitionedByProperty {
50414                    this: Box::new(schema),
50415                },
50416            ))));
50417        }
50418
50419        // Fall back to bracket(field)
50420        if let Some(bracket) = self.parse_bracket()? {
50421            return Ok(Some(Expression::PartitionedByProperty(Box::new(
50422                PartitionedByProperty {
50423                    this: Box::new(bracket),
50424                },
50425            ))));
50426        }
50427
50428        // Try to parse a field directly
50429        if let Some(field) = self.parse_field()? {
50430            return Ok(Some(Expression::PartitionedByProperty(Box::new(
50431                PartitionedByProperty {
50432                    this: Box::new(field),
50433                },
50434            ))));
50435        }
50436
50437        Ok(None)
50438    }
50439
50440    /// parse_partitioned_by_bucket_or_truncate - Parses BUCKET or TRUNCATE partition transforms
50441    /// Python: _parse_partitioned_by_bucket_or_truncate
50442    /// Syntax: BUCKET(col, num_buckets) or TRUNCATE(col, width)
50443    /// Handles both Hive (num, col) and Trino (col, num) ordering, normalizes to (col, num)
50444    pub fn parse_partitioned_by_bucket_or_truncate(&mut self) -> Result<Option<Expression>> {
50445        // If no L_PAREN follows, this should be parsed as an identifier, not a function call
50446        if !self.check(TokenType::LParen) {
50447            // Retreat: go back one token (previous was BUCKET or TRUNCATE)
50448            if self.current > 0 {
50449                self.current -= 1;
50450            }
50451            return Ok(None);
50452        }
50453
50454        // Determine if it's BUCKET or TRUNCATE based on previous token
50455        let is_bucket = self.previous().text.eq_ignore_ascii_case("BUCKET");
50456
50457        // Parse wrapped arguments
50458        self.expect(TokenType::LParen)?;
50459        let mut args = Vec::new();
50460
50461        if !self.check(TokenType::RParen) {
50462            loop {
50463                // Try to parse primary or column
50464                if let Some(expr) = self.parse_primary_or_var()? {
50465                    args.push(expr);
50466                } else if let Some(col) = self.parse_column()? {
50467                    args.push(col);
50468                }
50469
50470                if !self.match_token(TokenType::Comma) {
50471                    break;
50472                }
50473            }
50474        }
50475        self.match_token(TokenType::RParen);
50476
50477        // Get first two arguments
50478        let (mut this, mut expr) = (args.get(0).cloned(), args.get(1).cloned());
50479
50480        // Normalize: if first arg is a Literal, swap (Hive uses (num, col), Trino uses (col, num))
50481        // We canonicalize to (col, num)
50482        if let Some(Expression::Literal(_)) = &this {
50483            std::mem::swap(&mut this, &mut expr);
50484        }
50485
50486        // Ensure we have both arguments
50487        let this_expr = this.unwrap_or(Expression::Null(Null));
50488        let expr_expr = expr.unwrap_or(Expression::Null(Null));
50489
50490        if is_bucket {
50491            Ok(Some(Expression::PartitionedByBucket(Box::new(
50492                PartitionedByBucket {
50493                    this: Box::new(this_expr),
50494                    expression: Box::new(expr_expr),
50495                },
50496            ))))
50497        } else {
50498            Ok(Some(Expression::PartitionByTruncate(Box::new(
50499                PartitionByTruncate {
50500                    this: Box::new(this_expr),
50501                    expression: Box::new(expr_expr),
50502                },
50503            ))))
50504        }
50505    }
50506
50507    /// parse_doris_partition_by_range_or_list - Parses Doris PARTITION BY RANGE/LIST syntax
50508    /// Handles:
50509    ///   PARTITION BY RANGE (`col`) (PARTITION name VALUES LESS THAN (val), ...)
50510    ///   PARTITION BY RANGE (`col`) (PARTITION name VALUES [(val1), (val2)), ...)
50511    ///   PARTITION BY RANGE (`col`) (FROM ('start') TO ('end') INTERVAL n UNIT)
50512    ///   PARTITION BY LIST (`col`) (PARTITION name VALUES IN (val1, val2), ...)
50513    fn parse_doris_partition_by_range_or_list(&mut self, kind: &str) -> Result<Expression> {
50514        // Parse partition column expressions: (`col1`, `col2`, ...) or (STR2DATE(col, fmt))
50515        // Use parse_wrapped_csv to handle function calls in partition columns
50516        let partition_expressions = self.parse_wrapped_csv()?;
50517
50518        // Check for partition definitions in parentheses
50519        let create_expressions = if self.check(TokenType::LParen) {
50520            self.skip(); // consume (
50521
50522            if kind == "LIST" {
50523                // Parse LIST partition definitions: PARTITION name VALUES IN (val1, val2), ...
50524                let partitions = self.parse_doris_list_partition_definitions()?;
50525                self.expect(TokenType::RParen)?;
50526                Some(Box::new(Expression::Tuple(Box::new(Tuple {
50527                    expressions: partitions,
50528                }))))
50529            } else {
50530                // RANGE: check for FROM (dynamic), START (StarRocks dynamic), or PARTITION (static)
50531                if self.check(TokenType::From) {
50532                    // Dynamic: FROM ('start') TO ('end') INTERVAL n UNIT
50533                    let dynamic_expr = self.parse_doris_dynamic_partition()?;
50534                    self.expect(TokenType::RParen)?;
50535                    Some(Box::new(dynamic_expr))
50536                } else if self.check(TokenType::Start) {
50537                    // StarRocks dynamic: START ('val') END ('val') EVERY (expr), ...
50538                    let mut dynamics = Vec::new();
50539                    loop {
50540                        if !self.check(TokenType::Start) {
50541                            break;
50542                        }
50543                        let dynamic_expr = self.parse_starrocks_start_end_every()?;
50544                        dynamics.push(dynamic_expr);
50545                        if !self.match_token(TokenType::Comma) {
50546                            break;
50547                        }
50548                    }
50549                    self.expect(TokenType::RParen)?;
50550                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
50551                        expressions: dynamics,
50552                    }))))
50553                } else if self.check(TokenType::Partition) {
50554                    // Static: PARTITION name VALUES LESS THAN (val) or VALUES [(val1), (val2))
50555                    let partitions = self.parse_doris_range_partition_definitions()?;
50556                    self.expect(TokenType::RParen)?;
50557                    Some(Box::new(Expression::Tuple(Box::new(Tuple {
50558                        expressions: partitions,
50559                    }))))
50560                } else {
50561                    self.expect(TokenType::RParen)?;
50562                    None
50563                }
50564            }
50565        } else {
50566            None
50567        };
50568
50569        if kind == "LIST" {
50570            Ok(Expression::PartitionByListProperty(Box::new(
50571                PartitionByListProperty {
50572                    partition_expressions: partition_expressions.map(Box::new),
50573                    create_expressions,
50574                },
50575            )))
50576        } else {
50577            Ok(Expression::PartitionByRangeProperty(Box::new(
50578                PartitionByRangeProperty {
50579                    partition_expressions: partition_expressions.map(Box::new),
50580                    create_expressions,
50581                },
50582            )))
50583        }
50584    }
50585
50586    /// Parse Doris LIST partition definitions: PARTITION name VALUES IN (val1, val2), ...
50587    fn parse_doris_list_partition_definitions(&mut self) -> Result<Vec<Expression>> {
50588        let mut partitions = Vec::new();
50589        loop {
50590            if !self.match_token(TokenType::Partition) {
50591                break;
50592            }
50593            let name = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
50594            self.match_text_seq(&["VALUES", "IN"]);
50595            let values = self.parse_wrapped_csv_expressions()?;
50596
50597            let part_list = Expression::PartitionList(Box::new(PartitionList {
50598                this: Box::new(name),
50599                expressions: values,
50600            }));
50601            partitions.push(Expression::Partition(Box::new(Partition {
50602                expressions: vec![part_list],
50603                subpartition: false,
50604            })));
50605
50606            if !self.match_token(TokenType::Comma) {
50607                break;
50608            }
50609        }
50610        Ok(partitions)
50611    }
50612
50613    /// Parse Doris RANGE partition definitions
50614    fn parse_doris_range_partition_definitions(&mut self) -> Result<Vec<Expression>> {
50615        let mut partitions = Vec::new();
50616        loop {
50617            if !self.match_token(TokenType::Partition) {
50618                break;
50619            }
50620            let name = self.parse_id_var()?.unwrap_or(Expression::Null(Null));
50621            self.match_text_seq(&["VALUES"]);
50622
50623            let part_range = if self.match_text_seq(&["LESS", "THAN"]) {
50624                if self.match_token(TokenType::Maxvalue) {
50625                    // VALUES LESS THAN MAXVALUE (without parens)
50626                    Expression::PartitionRange(Box::new(PartitionRange {
50627                        this: Box::new(name),
50628                        expression: None,
50629                        expressions: vec![Expression::Identifier(Identifier::new("MAXVALUE"))],
50630                    }))
50631                } else {
50632                    // VALUES LESS THAN (val) or VALUES LESS THAN (MAXVALUE)
50633                    let values = self.parse_wrapped_csv_expressions()?;
50634                    Expression::PartitionRange(Box::new(PartitionRange {
50635                        this: Box::new(name),
50636                        expression: None,
50637                        expressions: values,
50638                    }))
50639                }
50640            } else if self.check(TokenType::LBracket) {
50641                // VALUES [(val1), (val2)) - note asymmetric brackets
50642                self.skip(); // consume [
50643                let mut value_tuples = Vec::new();
50644                loop {
50645                    let vals = self.parse_wrapped_csv_expressions()?;
50646                    // Wrap in a Tuple for each (val)
50647                    value_tuples.push(Expression::Tuple(Box::new(Tuple { expressions: vals })));
50648                    if !self.match_token(TokenType::Comma) {
50649                        break;
50650                    }
50651                }
50652                // Expect ) to close the asymmetric bracket
50653                self.expect(TokenType::RParen)?;
50654                Expression::PartitionRange(Box::new(PartitionRange {
50655                    this: Box::new(name),
50656                    expression: None,
50657                    expressions: value_tuples,
50658                }))
50659            } else {
50660                // Fallback: no values
50661                Expression::PartitionRange(Box::new(PartitionRange {
50662                    this: Box::new(name),
50663                    expression: None,
50664                    expressions: Vec::new(),
50665                }))
50666            };
50667
50668            partitions.push(Expression::Partition(Box::new(Partition {
50669                expressions: vec![part_range],
50670                subpartition: false,
50671            })));
50672
50673            if !self.match_token(TokenType::Comma) {
50674                break;
50675            }
50676        }
50677        Ok(partitions)
50678    }
50679
50680    /// Parse Doris dynamic partition: FROM ('start') TO ('end') INTERVAL n UNIT
50681    fn parse_doris_dynamic_partition(&mut self) -> Result<Expression> {
50682        self.expect(TokenType::From)?;
50683        let start = self.parse_wrapped_expression()?;
50684        self.expect(TokenType::To)?;
50685        let end = self.parse_wrapped_expression()?;
50686
50687        // Parse INTERVAL n UNIT
50688        let every = if self.match_token(TokenType::Interval) {
50689            let number = self.parse_expression()?;
50690            let unit = if self.is_identifier_token() || self.is_safe_keyword_as_identifier() {
50691                let unit_text = self.advance().text.to_ascii_uppercase();
50692                // Convert unit text to IntervalUnit
50693                let interval_unit = match unit_text.as_str() {
50694                    "YEAR" | "YEARS" => crate::expressions::IntervalUnit::Year,
50695                    "MONTH" | "MONTHS" => crate::expressions::IntervalUnit::Month,
50696                    "DAY" | "DAYS" => crate::expressions::IntervalUnit::Day,
50697                    "HOUR" | "HOURS" => crate::expressions::IntervalUnit::Hour,
50698                    "MINUTE" | "MINUTES" => crate::expressions::IntervalUnit::Minute,
50699                    "SECOND" | "SECONDS" => crate::expressions::IntervalUnit::Second,
50700                    _ => crate::expressions::IntervalUnit::Day, // Default fallback
50701                };
50702                Some(crate::expressions::IntervalUnitSpec::Simple {
50703                    unit: interval_unit,
50704                    use_plural: unit_text.ends_with('S'),
50705                })
50706            } else {
50707                None
50708            };
50709            Some(Box::new(Expression::Interval(Box::new(Interval {
50710                this: Some(number),
50711                unit,
50712            }))))
50713        } else {
50714            None
50715        };
50716
50717        Ok(Expression::PartitionByRangePropertyDynamic(Box::new(
50718            PartitionByRangePropertyDynamic {
50719                this: None,
50720                start: Some(Box::new(start)),
50721                end: Some(Box::new(end)),
50722                every,
50723                use_start_end: false,
50724            },
50725        )))
50726    }
50727
50728    /// Parse StarRocks START ('val') END ('val') EVERY (expr) syntax
50729    fn parse_starrocks_start_end_every(&mut self) -> Result<Expression> {
50730        self.expect(TokenType::Start)?;
50731        let start = self.parse_wrapped_expression()?;
50732        self.expect(TokenType::End)?;
50733        let end = self.parse_wrapped_expression()?;
50734
50735        // Parse EVERY (expr)
50736        let every = if self.match_identifier("EVERY") {
50737            self.expect(TokenType::LParen)?;
50738            let expr = self.parse_expression()?;
50739            self.expect(TokenType::RParen)?;
50740            Some(Box::new(expr))
50741        } else {
50742            None
50743        };
50744
50745        Ok(Expression::PartitionByRangePropertyDynamic(Box::new(
50746            PartitionByRangePropertyDynamic {
50747                this: None,
50748                start: Some(Box::new(start)),
50749                end: Some(Box::new(end)),
50750                every,
50751                use_start_end: true,
50752            },
50753        )))
50754    }
50755
50756    /// Parse wrapped comma-separated expressions: (expr, expr, ...)
50757    fn parse_wrapped_csv_expressions(&mut self) -> Result<Vec<Expression>> {
50758        self.expect(TokenType::LParen)?;
50759        let mut exprs = Vec::new();
50760        if !self.check(TokenType::RParen) {
50761            loop {
50762                // Check for MAXVALUE special keyword
50763                if self.match_token(TokenType::Maxvalue) {
50764                    exprs.push(Expression::Var(Box::new(Var {
50765                        this: "MAXVALUE".to_string(),
50766                    })));
50767                } else {
50768                    exprs.push(self.parse_expression()?);
50769                }
50770                if !self.match_token(TokenType::Comma) {
50771                    break;
50772                }
50773            }
50774        }
50775        self.expect(TokenType::RParen)?;
50776        Ok(exprs)
50777    }
50778
50779    /// Parse a single wrapped expression: (expr)
50780    fn parse_wrapped_expression(&mut self) -> Result<Expression> {
50781        self.expect(TokenType::LParen)?;
50782        let expr = self.parse_expression()?;
50783        self.expect(TokenType::RParen)?;
50784        Ok(expr)
50785    }
50786
50787    /// parse_partitioned_of - Implemented from Python _parse_partitioned_of
50788    #[allow(unused_variables, unused_mut)]
50789    pub fn parse_partitioned_of(&mut self) -> Result<Option<Expression>> {
50790        if self.match_text_seq(&["OF"]) {
50791            return Ok(Some(Expression::PartitionBoundSpec(Box::new(
50792                PartitionBoundSpec {
50793                    this: None,
50794                    expression: None,
50795                    from_expressions: None,
50796                    to_expressions: None,
50797                },
50798            ))));
50799        }
50800        if self.match_text_seq(&["FOR", "VALUES"]) {
50801            // Matched: FOR VALUES
50802            return Ok(None);
50803        }
50804        Ok(None)
50805    }
50806
50807    /// parse_period_for_system_time - Parses PERIOD FOR SYSTEM_TIME constraint
50808    /// Python: _parse_period_for_system_time
50809    /// Syntax: PERIOD FOR SYSTEM_TIME (start_col, end_col)
50810    pub fn parse_period_for_system_time(&mut self) -> Result<Option<Expression>> {
50811        // Check for SYSTEM_TIME / TIMESTAMP_SNAPSHOT token
50812        if !self.match_token(TokenType::TimestampSnapshot) {
50813            // Retreat: go back one token
50814            if self.current > 0 {
50815                self.current -= 1;
50816            }
50817            return Ok(None);
50818        }
50819
50820        // Parse wrapped id vars (two column names)
50821        let id_vars = self.parse_wrapped_id_vars()?;
50822
50823        // Extract the two columns from the tuple
50824        let (this, expression) = if let Some(Expression::Tuple(tuple)) = id_vars {
50825            let exprs = &tuple.expressions;
50826            (
50827                exprs.get(0).cloned().unwrap_or(Expression::Null(Null)),
50828                exprs.get(1).cloned().unwrap_or(Expression::Null(Null)),
50829            )
50830        } else {
50831            return Ok(None);
50832        };
50833
50834        Ok(Some(Expression::PeriodForSystemTimeConstraint(Box::new(
50835            PeriodForSystemTimeConstraint {
50836                this: Box::new(this),
50837                expression: Box::new(expression),
50838            },
50839        ))))
50840    }
50841
50842    /// parse_pipe_syntax_aggregate - Implemented from Python _parse_pipe_syntax_aggregate
50843    #[allow(unused_variables, unused_mut)]
50844    pub fn parse_pipe_syntax_aggregate(&mut self) -> Result<Option<Expression>> {
50845        if self.match_text_seq(&["AGGREGATE"]) {
50846            return Ok(Some(Expression::Select(Box::new(Select {
50847                expressions: Vec::new(),
50848                from: None,
50849                joins: Vec::new(),
50850                lateral_views: Vec::new(),
50851                prewhere: None,
50852                where_clause: None,
50853                group_by: None,
50854                having: None,
50855                qualify: None,
50856                order_by: None,
50857                distribute_by: None,
50858                cluster_by: None,
50859                sort_by: None,
50860                limit: None,
50861                offset: None,
50862                limit_by: None,
50863                fetch: None,
50864                distinct: false,
50865                distinct_on: None,
50866                top: None,
50867                with: None,
50868                sample: None,
50869                settings: None,
50870                format: None,
50871                windows: None,
50872                hint: None,
50873                connect: None,
50874                into: None,
50875                locks: Vec::new(),
50876                for_xml: Vec::new(),
50877                leading_comments: Vec::new(),
50878                post_select_comments: Vec::new(),
50879                kind: None,
50880                operation_modifiers: Vec::new(),
50881                qualify_after_window: false,
50882                option: None,
50883                exclude: None,
50884            }))));
50885        }
50886        if self.match_text_seq(&["GROUP", "AND"]) {
50887            // Matched: GROUP AND
50888            return Ok(None);
50889        }
50890        Ok(None)
50891    }
50892
50893    /// parse_pipe_syntax_aggregate_fields - Implemented from Python _parse_pipe_syntax_aggregate_fields
50894    /// Calls: parse_disjunction
50895    #[allow(unused_variables, unused_mut)]
50896    pub fn parse_pipe_syntax_aggregate_fields(&mut self) -> Result<Option<Expression>> {
50897        if self.match_text_seq(&["GROUP", "AND"]) {
50898            // Matched: GROUP AND
50899            return Ok(None);
50900        }
50901        Ok(None)
50902    }
50903
50904    /// parse_pipe_syntax_aggregate_group_order_by - Parses pipe syntax aggregate fields with grouping and ordering
50905    /// Python: _parse_pipe_syntax_aggregate_group_order_by
50906    /// Parses comma-separated aggregate fields and separates them into aggregates/groups and ORDER BY specs
50907    /// Returns a Tuple with two elements: (aggregates_and_groups, order_by_specs)
50908    pub fn parse_pipe_syntax_aggregate_group_order_by(&mut self) -> Result<Option<Expression>> {
50909        // Parse CSV of pipe syntax aggregate fields
50910        let mut aggregates_or_groups = Vec::new();
50911        let mut orders = Vec::new();
50912
50913        loop {
50914            if let Some(element) = self.parse_pipe_syntax_aggregate_fields()? {
50915                // Check if it's an Ordered expression (ORDER BY spec)
50916                match &element {
50917                    Expression::Ordered(ordered) => {
50918                        // Extract the inner expression, potentially adjusting for alias
50919                        let this = match &ordered.this {
50920                            Expression::Alias(alias) => {
50921                                // Use the alias name as an Identifier expression
50922                                Expression::Identifier(alias.alias.clone())
50923                            }
50924                            other => other.clone(),
50925                        };
50926                        // Add modified Ordered to orders
50927                        orders.push(Expression::Ordered(Box::new(Ordered {
50928                            this: this.clone(),
50929                            desc: ordered.desc,
50930                            nulls_first: ordered.nulls_first,
50931                            explicit_asc: ordered.explicit_asc,
50932                            with_fill: ordered.with_fill.clone(),
50933                        })));
50934                        aggregates_or_groups.push(this);
50935                    }
50936                    _ => {
50937                        aggregates_or_groups.push(element);
50938                    }
50939                }
50940            }
50941
50942            if !self.match_token(TokenType::Comma) {
50943                break;
50944            }
50945        }
50946
50947        if aggregates_or_groups.is_empty() && orders.is_empty() {
50948            return Ok(None);
50949        }
50950
50951        // Return a tuple with (aggregates_or_groups, orders)
50952        Ok(Some(Expression::Tuple(Box::new(Tuple {
50953            expressions: vec![
50954                Expression::Tuple(Box::new(Tuple {
50955                    expressions: aggregates_or_groups,
50956                })),
50957                Expression::Tuple(Box::new(Tuple {
50958                    expressions: orders,
50959                })),
50960            ],
50961        }))))
50962    }
50963
50964    /// parse_pipe_syntax_extend - Implemented from Python _parse_pipe_syntax_extend
50965    #[allow(unused_variables, unused_mut)]
50966    pub fn parse_pipe_syntax_extend(&mut self) -> Result<Option<Expression>> {
50967        if self.match_text_seq(&["EXTEND"]) {
50968            return Ok(Some(Expression::Select(Box::new(Select {
50969                expressions: Vec::new(),
50970                from: None,
50971                joins: Vec::new(),
50972                lateral_views: Vec::new(),
50973                prewhere: None,
50974                where_clause: None,
50975                group_by: None,
50976                having: None,
50977                qualify: None,
50978                order_by: None,
50979                distribute_by: None,
50980                cluster_by: None,
50981                sort_by: None,
50982                limit: None,
50983                offset: None,
50984                limit_by: None,
50985                fetch: None,
50986                distinct: false,
50987                distinct_on: None,
50988                top: None,
50989                with: None,
50990                sample: None,
50991                settings: None,
50992                format: None,
50993                windows: None,
50994                hint: None,
50995                connect: None,
50996                into: None,
50997                locks: Vec::new(),
50998                for_xml: Vec::new(),
50999                leading_comments: Vec::new(),
51000                post_select_comments: Vec::new(),
51001                kind: None,
51002                operation_modifiers: Vec::new(),
51003                qualify_after_window: false,
51004                option: None,
51005                exclude: None,
51006            }))));
51007        }
51008        Ok(None)
51009    }
51010
51011    /// parse_pipe_syntax_join - Parses JOIN in BigQuery pipe syntax
51012    /// Python: _parse_pipe_syntax_join
51013    /// Format: |> JOIN table ON condition
51014    pub fn parse_pipe_syntax_join(&mut self) -> Result<Option<Expression>> {
51015        // Parse the JOIN clause
51016        self.parse_join()
51017    }
51018
51019    /// parse_pipe_syntax_limit - Parses LIMIT/OFFSET in BigQuery pipe syntax
51020    /// Python: _parse_pipe_syntax_limit
51021    /// Format: |> LIMIT n [OFFSET m]
51022    pub fn parse_pipe_syntax_limit(&mut self) -> Result<Option<Expression>> {
51023        // Parse the LIMIT clause
51024        let limit = self.parse_limit()?;
51025
51026        // Parse optional OFFSET
51027        let offset = self.parse_offset()?;
51028
51029        // Combine into a tuple if both present
51030        match (limit, offset) {
51031            (Some(l), Some(o)) => Ok(Some(Expression::Tuple(Box::new(Tuple {
51032                expressions: vec![l, o],
51033            })))),
51034            (Some(l), None) => Ok(Some(l)),
51035            (None, Some(o)) => Ok(Some(o)),
51036            (None, None) => Ok(None),
51037        }
51038    }
51039
51040    /// parse_pipe_syntax_pivot - Parses PIVOT in BigQuery pipe syntax
51041    /// Python: _parse_pipe_syntax_pivot
51042    /// Format: |> PIVOT (agg_function FOR column IN (values))
51043    pub fn parse_pipe_syntax_pivot(&mut self) -> Result<Option<Expression>> {
51044        // For pipe syntax, we don't have a source yet - return pivot aggregation
51045        // The actual pivot parsing will be done in the query transformer
51046        self.parse_pivot_aggregation()
51047    }
51048
51049    /// parse_pipe_syntax_query - Parses a query with pipe syntax transformations
51050    /// Python: _parse_pipe_syntax_query
51051    /// Handles queries like: FROM table |> WHERE ... |> SELECT ... |> AGGREGATE ...
51052    pub fn parse_pipe_syntax_query(&mut self) -> Result<Option<Expression>> {
51053        // Start with a base query (could be a FROM clause or subquery)
51054        let mut query = self.parse_select_query()?;
51055
51056        if query.is_none() {
51057            return Ok(None);
51058        }
51059
51060        // Process pipe syntax chain: |> transform1 |> transform2 |> ...
51061        while self.match_token(TokenType::PipeGt) {
51062            let start_pos = self.current;
51063            let operator_text = self.peek().text.to_ascii_uppercase();
51064
51065            // Try to match known pipe syntax transforms
51066            let transform_result = match operator_text.as_str() {
51067                "WHERE" => {
51068                    self.skip();
51069                    self.parse_where()?
51070                }
51071                "SELECT" => {
51072                    self.skip();
51073                    self.parse_pipe_syntax_select()?
51074                }
51075                "AGGREGATE" => {
51076                    self.skip();
51077                    self.parse_pipe_syntax_aggregate()?
51078                }
51079                "EXTEND" => {
51080                    self.skip();
51081                    self.parse_pipe_syntax_extend()?
51082                }
51083                "LIMIT" => {
51084                    self.skip();
51085                    self.parse_pipe_syntax_limit()?
51086                }
51087                "JOIN" | "LEFT" | "RIGHT" | "INNER" | "OUTER" | "CROSS" | "FULL" => {
51088                    self.parse_pipe_syntax_join()?
51089                }
51090                "UNION" | "INTERSECT" | "EXCEPT" => self.parse_pipe_syntax_set_operator()?,
51091                "PIVOT" => {
51092                    self.skip();
51093                    self.parse_pipe_syntax_pivot()?
51094                }
51095                "TABLESAMPLE" => {
51096                    self.skip();
51097                    self.parse_pipe_syntax_tablesample()?
51098                }
51099                _ => {
51100                    // Try set operator or join as fallback
51101                    let set_op = self.parse_pipe_syntax_set_operator()?;
51102                    if set_op.is_some() {
51103                        set_op
51104                    } else {
51105                        let join_op = self.parse_pipe_syntax_join()?;
51106                        if join_op.is_some() {
51107                            join_op
51108                        } else {
51109                            // Unsupported operator, retreat and break
51110                            self.current = start_pos;
51111                            break;
51112                        }
51113                    }
51114                }
51115            };
51116
51117            // Apply transform to query
51118            if let Some(transform) = transform_result {
51119                // Wrap current query with transform in a PipeOperator
51120                let current_query = query.ok_or_else(|| {
51121                    self.parse_error("Expected base query before pipe syntax transform")
51122                })?;
51123                query = Some(Expression::PipeOperator(Box::new(PipeOperator {
51124                    this: current_query,
51125                    expression: transform,
51126                })));
51127            }
51128        }
51129
51130        Ok(query)
51131    }
51132
51133    /// parse_pipe_syntax_select - Parses SELECT in BigQuery pipe syntax
51134    /// Python: _parse_pipe_syntax_select
51135    /// Format: |> SELECT expressions
51136    pub fn parse_pipe_syntax_select(&mut self) -> Result<Option<Expression>> {
51137        // Parse the SELECT expressions without consuming the pipe
51138        let expressions = self.parse_expressions()?;
51139
51140        match expressions {
51141            Some(expr) => Ok(Some(expr)),
51142            None => Ok(Some(Expression::Star(Star {
51143                table: None,
51144                except: None,
51145                replace: None,
51146                rename: None,
51147                trailing_comments: Vec::new(),
51148                span: None,
51149            }))),
51150        }
51151    }
51152
51153    /// parse_pipe_syntax_set_operator - Parses set operation in BigQuery pipe syntax
51154    /// Python: _parse_pipe_syntax_set_operator
51155    /// Format: |> UNION ALL/INTERSECT/EXCEPT (subquery1, subquery2, ...)
51156    pub fn parse_pipe_syntax_set_operator(&mut self) -> Result<Option<Expression>> {
51157        // Try to parse as a set operation (UNION, INTERSECT, EXCEPT)
51158        if let Some(set_op) = self.parse_set_operations()? {
51159            Ok(Some(set_op))
51160        } else {
51161            Ok(None)
51162        }
51163    }
51164
51165    /// parse_pipe_syntax_tablesample - Parses TABLESAMPLE in BigQuery pipe syntax
51166    /// Python: _parse_pipe_syntax_tablesample
51167    /// Format: |> TABLESAMPLE SYSTEM (percent PERCENT)
51168    pub fn parse_pipe_syntax_tablesample(&mut self) -> Result<Option<Expression>> {
51169        // Parse the TABLESAMPLE clause
51170        self.parse_table_sample()
51171    }
51172
51173    /// parse_pivot_aggregation - Ported from Python _parse_pivot_aggregation
51174    /// Parses an aggregation function in PIVOT clause, optionally with alias
51175    #[allow(unused_variables, unused_mut)]
51176    pub fn parse_pivot_aggregation(&mut self) -> Result<Option<Expression>> {
51177        // Parse a function
51178        let func = self.parse_function()?;
51179
51180        if func.is_none() {
51181            // If previous token was a comma, silently return None
51182            if self.previous().token_type == TokenType::Comma {
51183                return Ok(None);
51184            }
51185            // Otherwise this could be an error, but we'll just return None
51186            return Ok(None);
51187        }
51188
51189        // Try to parse an alias for the function
51190        self.parse_alias_with_expr(func)
51191    }
51192
51193    /// parse_pivot_in - Parses the IN clause of a PIVOT
51194    /// Python: _parse_pivot_in
51195    /// Format: column IN (value1 [AS alias1], value2 [AS alias2], ...)
51196    pub fn parse_pivot_in(&mut self) -> Result<Option<Expression>> {
51197        // Parse the column being pivoted
51198        let value = self.parse_column()?;
51199        let value_expr = value.unwrap_or(Expression::Null(Null));
51200
51201        // Expect IN keyword
51202        if !self.match_token(TokenType::In) {
51203            return Err(self.parse_error("Expecting IN"));
51204        }
51205
51206        // Check if it's a parenthesized list or a field reference
51207        if self.match_token(TokenType::LParen) {
51208            // Check for ANY keyword
51209            let expressions = if self.match_text_seq(&["ANY"]) {
51210                // Parse PivotAny with optional ORDER BY
51211                let order = self.parse_order()?;
51212                vec![Expression::PivotAny(Box::new(PivotAny {
51213                    this: order.map(Box::new),
51214                }))]
51215            } else {
51216                // Parse comma-separated list of expressions, optionally aliased
51217                let mut exprs = Vec::new();
51218                loop {
51219                    if let Some(expr) = self.parse_select_or_expression()? {
51220                        // Check for alias
51221                        let final_expr = if self.match_token(TokenType::Alias) {
51222                            if let Some(alias) = self.parse_bitwise()? {
51223                                // Store the alias expression directly
51224                                Expression::PivotAlias(Box::new(PivotAlias { this: expr, alias }))
51225                            } else {
51226                                expr
51227                            }
51228                        } else {
51229                            expr
51230                        };
51231                        exprs.push(final_expr);
51232                    } else {
51233                        break;
51234                    }
51235                    if !self.match_token(TokenType::Comma) {
51236                        break;
51237                    }
51238                }
51239                exprs
51240            };
51241
51242            self.expect(TokenType::RParen)?;
51243
51244            Ok(Some(Expression::In(Box::new(In {
51245                this: value_expr,
51246                expressions,
51247                query: None,
51248                not: false,
51249                global: false,
51250                unnest: None,
51251                is_field: false,
51252            }))))
51253        } else {
51254            // Parse as a field reference: IN field_name
51255            let field = self.parse_id_var()?;
51256            // Convert field to expression and add to expressions
51257            let expressions = if let Some(f) = field {
51258                vec![f]
51259            } else {
51260                Vec::new()
51261            };
51262            Ok(Some(Expression::In(Box::new(In {
51263                this: value_expr,
51264                expressions,
51265                query: None,
51266                not: false,
51267                global: false,
51268                unnest: None,
51269                is_field: true,
51270            }))))
51271        }
51272    }
51273
51274    /// parse_pivots - Ported from Python _parse_pivots
51275    /// Parses one or more PIVOT/UNPIVOT clauses attached to a source expression
51276    /// Uses the existing parse_pivot/parse_unpivot methods
51277    pub fn parse_pivots_for_source(&mut self, source: Expression) -> Result<Option<Expression>> {
51278        let mut result = source;
51279
51280        loop {
51281            if self.match_token(TokenType::Pivot) {
51282                result = self.parse_pivot(result)?;
51283            } else if self.match_texts(&["UNPIVOT"]) {
51284                result = self.parse_unpivot(result)?;
51285            } else {
51286                break;
51287            }
51288        }
51289
51290        // Return None if no pivots were parsed
51291        if matches!(result, Expression::Null(_)) {
51292            Ok(None)
51293        } else {
51294            Ok(Some(result))
51295        }
51296    }
51297
51298    /// parse_placeholder - Parse placeholder token (? or :name)
51299    /// Python: if self._match_set(self.PLACEHOLDER_PARSERS): return placeholder
51300    pub fn parse_placeholder(&mut self) -> Result<Option<Expression>> {
51301        // Match positional placeholder (?)
51302        if self.match_token(TokenType::Placeholder) {
51303            return Ok(Some(Expression::Placeholder(Placeholder { index: None })));
51304        }
51305        // Match colon placeholder (:name) - handled by Parameter token
51306        if self.match_token(TokenType::Parameter) {
51307            let text = self.previous().text.clone();
51308            return Ok(Some(Expression::Parameter(Box::new(Parameter {
51309                name: Some(text),
51310                index: None,
51311                style: ParameterStyle::Colon,
51312                quoted: false,
51313                string_quoted: false,
51314                expression: None,
51315            }))));
51316        }
51317        Ok(None)
51318    }
51319
51320    /// Parse ClickHouse query parameter syntax: {name: Type}
51321    fn parse_clickhouse_braced_parameter(&mut self) -> Result<Option<Expression>> {
51322        if !matches!(
51323            self.config.dialect,
51324            Some(crate::dialects::DialectType::ClickHouse)
51325        ) {
51326            return Ok(None);
51327        }
51328        if !self.check(TokenType::LBrace) {
51329            return Ok(None);
51330        }
51331
51332        let start = self.current;
51333        self.skip(); // consume {
51334
51335        if !(self.is_identifier_token() || self.is_safe_keyword_as_identifier()) {
51336            self.current = start;
51337            return Ok(None);
51338        }
51339        let name = self.advance().text.clone();
51340
51341        if !self.match_token(TokenType::Colon) {
51342            self.current = start;
51343            return Ok(None);
51344        }
51345
51346        let kind_start = self.current;
51347        let mut paren_depth = 0usize;
51348        let mut bracket_depth = 0usize;
51349
51350        while !self.is_at_end() {
51351            let token_type = self.peek().token_type;
51352            match token_type {
51353                TokenType::LParen => {
51354                    paren_depth += 1;
51355                    self.skip();
51356                }
51357                TokenType::RParen => {
51358                    if paren_depth == 0 {
51359                        break;
51360                    }
51361                    paren_depth -= 1;
51362                    self.skip();
51363                }
51364                TokenType::LBracket => {
51365                    bracket_depth += 1;
51366                    self.skip();
51367                }
51368                TokenType::RBracket => {
51369                    if bracket_depth == 0 {
51370                        break;
51371                    }
51372                    bracket_depth -= 1;
51373                    self.skip();
51374                }
51375                TokenType::RBrace => {
51376                    if paren_depth == 0 && bracket_depth == 0 {
51377                        break;
51378                    }
51379                    self.skip();
51380                }
51381                _ => {
51382                    self.skip();
51383                }
51384            }
51385        }
51386
51387        if self.current <= kind_start || !self.match_token(TokenType::RBrace) {
51388            return Err(self.parse_error("Expected } in ClickHouse query parameter"));
51389        }
51390
51391        let kind = self
51392            .tokens_to_sql(kind_start, self.current - 1)
51393            .trim()
51394            .to_string();
51395        if kind.is_empty() {
51396            return Err(self.parse_error("Expected parameter kind in ClickHouse query parameter"));
51397        }
51398
51399        Ok(Some(Expression::Parameter(Box::new(Parameter {
51400            name: Some(name),
51401            index: None,
51402            style: ParameterStyle::Brace,
51403            quoted: false,
51404            string_quoted: false,
51405            expression: Some(kind),
51406        }))))
51407    }
51408
51409    /// parse_position - Ported from Python _parse_position
51410    /// Parses POSITION function: POSITION(substr IN str) or POSITION(needle, haystack, start)
51411    #[allow(unused_variables, unused_mut)]
51412    pub fn parse_position(&mut self) -> Result<Option<Expression>> {
51413        // Parse comma-separated arguments first
51414        let mut args: Vec<Expression> = Vec::new();
51415
51416        match self.parse_bitwise() {
51417            Ok(Some(expr)) => {
51418                let expr = self.maybe_clickhouse_alias(expr);
51419                let expr = self.try_clickhouse_func_arg_alias(expr);
51420                args.push(expr);
51421            }
51422            Ok(None) => return Ok(None),
51423            Err(e) => return Err(e),
51424        }
51425
51426        // Check for IN keyword (SQL standard syntax: POSITION(substr IN str))
51427        if self.match_token(TokenType::In) {
51428            match self.parse_bitwise() {
51429                Ok(Some(haystack)) => {
51430                    let haystack = self.maybe_clickhouse_alias(haystack);
51431                    let haystack = self.try_clickhouse_func_arg_alias(haystack);
51432                    return Ok(Some(Expression::StrPosition(Box::new(StrPosition {
51433                        this: Box::new(haystack),
51434                        substr: Some(Box::new(args.remove(0))),
51435                        position: None,
51436                        occurrence: None,
51437                    }))));
51438                }
51439                Ok(None) => {
51440                    return Err(self.parse_error("Expected expression after IN in POSITION"))
51441                }
51442                Err(e) => return Err(e),
51443            }
51444        }
51445
51446        // Parse comma-separated additional arguments
51447        while self.match_token(TokenType::Comma) {
51448            match self.parse_bitwise() {
51449                Ok(Some(expr)) => {
51450                    let expr = self.maybe_clickhouse_alias(expr);
51451                    let expr = self.try_clickhouse_func_arg_alias(expr);
51452                    args.push(expr);
51453                }
51454                Ok(None) => break,
51455                Err(e) => return Err(e),
51456            }
51457        }
51458
51459        // Function syntax: POSITION(needle, haystack, start?) or ClickHouse POSITION(haystack, needle, start?)
51460        let position = args.get(2).cloned();
51461        let (haystack, needle) = if matches!(
51462            self.config.dialect,
51463            Some(crate::dialects::DialectType::ClickHouse)
51464        ) {
51465            (args.get(0).cloned(), args.get(1).cloned())
51466        } else {
51467            (args.get(1).cloned(), args.get(0).cloned())
51468        };
51469
51470        Ok(Some(Expression::StrPosition(Box::new(StrPosition {
51471            this: Box::new(
51472                haystack.unwrap_or_else(|| {
51473                    Expression::Literal(Box::new(Literal::String("".to_string())))
51474                }),
51475            ),
51476            substr: needle.map(Box::new),
51477            position: position.map(Box::new),
51478            occurrence: None,
51479        }))))
51480    }
51481
51482    /// parse_prewhere - Ported from Python _parse_prewhere
51483    /// Parses PREWHERE clause (ClickHouse specific)
51484    #[allow(unused_variables, unused_mut)]
51485    pub fn parse_prewhere(&mut self) -> Result<Option<Expression>> {
51486        if !self.match_token(TokenType::Prewhere) {
51487            return Ok(None);
51488        }
51489        // Parse the condition expression
51490        let condition = self.parse_expression()?;
51491        Ok(Some(Expression::PreWhere(Box::new(PreWhere {
51492            this: condition,
51493        }))))
51494    }
51495
51496    /// parse_primary_key - Parses PRIMARY KEY constraint
51497    /// Python: _parse_primary_key
51498    /// Can return either PrimaryKeyColumnConstraint (column-level) or PrimaryKey (table-level)
51499    pub fn parse_primary_key(&mut self) -> Result<Option<Expression>> {
51500        self.parse_primary_key_impl(false, false)
51501    }
51502
51503    /// Implementation of parse_primary_key with options
51504    pub fn parse_primary_key_impl(
51505        &mut self,
51506        wrapped_optional: bool,
51507        in_props: bool,
51508    ) -> Result<Option<Expression>> {
51509        // Check for ASC/DESC
51510        let desc = if self.match_token(TokenType::Asc) {
51511            false
51512        } else if self.match_token(TokenType::Desc) {
51513            true
51514        } else {
51515            false
51516        };
51517
51518        // Parse optional constraint name (if current token is identifier and next is L_PAREN)
51519        let this = if (self.check(TokenType::Identifier) || self.check(TokenType::Var))
51520            && self.check_next(TokenType::LParen)
51521        {
51522            self.parse_id_var()?
51523        } else {
51524            None
51525        };
51526
51527        // If not in_props and no L_PAREN ahead, return column-level constraint
51528        if !in_props && !self.check(TokenType::LParen) {
51529            let options = self.parse_key_constraint_options_list()?;
51530            return Ok(Some(Expression::PrimaryKeyColumnConstraint(Box::new(
51531                PrimaryKeyColumnConstraint {
51532                    desc: if desc {
51533                        Some(Box::new(Expression::Boolean(BooleanLiteral {
51534                            value: true,
51535                        })))
51536                    } else {
51537                        None
51538                    },
51539                    options,
51540                },
51541            ))));
51542        }
51543
51544        // Parse table-level PRIMARY KEY (column_list)
51545        let expressions = if self.match_token(TokenType::LParen) {
51546            let mut exprs = Vec::new();
51547            loop {
51548                if let Some(part) = self.parse_primary_key_part()? {
51549                    exprs.push(part);
51550                }
51551                if !self.match_token(TokenType::Comma) {
51552                    break;
51553                }
51554            }
51555            self.expect(TokenType::RParen)?;
51556            exprs
51557        } else if wrapped_optional {
51558            Vec::new()
51559        } else {
51560            return Err(self.parse_error("Expected '(' for PRIMARY KEY column list"));
51561        };
51562
51563        // Parse INCLUDE clause for covering index
51564        let include = self.parse_index_params()?;
51565
51566        // Parse constraint options
51567        let options = self.parse_key_constraint_options_list()?;
51568
51569        Ok(Some(Expression::PrimaryKey(Box::new(PrimaryKey {
51570            this: this.map(Box::new),
51571            expressions,
51572            options,
51573            include: include.map(Box::new),
51574        }))))
51575    }
51576
51577    /// Parse key constraint options as a list of expressions
51578    fn parse_key_constraint_options_list(&mut self) -> Result<Vec<Expression>> {
51579        let mut options = Vec::new();
51580
51581        loop {
51582            if self.is_at_end() {
51583                break;
51584            }
51585
51586            if self.match_token(TokenType::On) {
51587                // Parse ON DELETE/UPDATE action
51588                let on_what = if !self.is_at_end() {
51589                    let token = self.advance();
51590                    token.text.clone()
51591                } else {
51592                    break;
51593                };
51594
51595                let action = if self.match_text_seq(&["NO", "ACTION"]) {
51596                    "NO ACTION"
51597                } else if self.match_text_seq(&["CASCADE"]) {
51598                    "CASCADE"
51599                } else if self.match_text_seq(&["RESTRICT"]) {
51600                    "RESTRICT"
51601                } else if self.match_token(TokenType::Set) && self.match_token(TokenType::Null) {
51602                    "SET NULL"
51603                } else if self.match_token(TokenType::Set) && self.match_token(TokenType::Default) {
51604                    "SET DEFAULT"
51605                } else {
51606                    break;
51607                };
51608
51609                options.push(Expression::Var(Box::new(Var {
51610                    this: format!("ON {} {}", on_what, action),
51611                })));
51612            } else if self.match_text_seq(&["NOT", "ENFORCED"]) {
51613                options.push(Expression::Var(Box::new(Var {
51614                    this: "NOT ENFORCED".to_string(),
51615                })));
51616            } else if self.match_text_seq(&["DEFERRABLE"]) {
51617                options.push(Expression::Var(Box::new(Var {
51618                    this: "DEFERRABLE".to_string(),
51619                })));
51620            } else if self.match_text_seq(&["INITIALLY", "DEFERRED"]) {
51621                options.push(Expression::Var(Box::new(Var {
51622                    this: "INITIALLY DEFERRED".to_string(),
51623                })));
51624            } else if self.match_text_seq(&["NORELY"]) {
51625                options.push(Expression::Var(Box::new(Var {
51626                    this: "NORELY".to_string(),
51627                })));
51628            } else if self.match_text_seq(&["RELY"]) {
51629                options.push(Expression::Var(Box::new(Var {
51630                    this: "RELY".to_string(),
51631                })));
51632            } else {
51633                break;
51634            }
51635        }
51636
51637        Ok(options)
51638    }
51639
51640    /// parse_primary_key_part - Delegates to parse_field
51641    #[allow(unused_variables, unused_mut)]
51642    pub fn parse_primary_key_part(&mut self) -> Result<Option<Expression>> {
51643        // ClickHouse: PRIMARY KEY can contain full expressions (e.g., t.a, c0 IN (SELECT 1))
51644        if matches!(
51645            self.config.dialect,
51646            Some(crate::dialects::DialectType::ClickHouse)
51647        ) {
51648            return self.parse_expression().map(Some);
51649        }
51650        if (self.is_identifier_token() || self.is_safe_keyword_as_identifier())
51651            && self.check_next(TokenType::LParen)
51652        {
51653            return self.parse_expression().map(Some);
51654        }
51655        if let Some(field) = self.parse_field()? {
51656            Ok(Some(field))
51657        } else {
51658            self.parse_expression().map(Some)
51659        }
51660    }
51661
51662    /// parse_primary_or_var - Parses a primary expression or variable
51663    /// Python: _parse_primary_or_var
51664    /// Returns: parse_primary() or parse_var(any_token=True)
51665    pub fn parse_primary_or_var(&mut self) -> Result<Option<Expression>> {
51666        // First try to parse a primary expression
51667        let saved_pos = self.current;
51668        match self.parse_primary() {
51669            Ok(expr) => return Ok(Some(expr)),
51670            Err(_) => {
51671                // Reset position and try parse_var
51672                self.current = saved_pos;
51673            }
51674        }
51675
51676        // Fall back to parsing a variable
51677        self.parse_var()
51678    }
51679
51680    /// parse_procedure_option - Implemented from Python _parse_procedure_option
51681    #[allow(unused_variables, unused_mut)]
51682    pub fn parse_procedure_option(&mut self) -> Result<Option<Expression>> {
51683        if self.match_text_seq(&["EXECUTE", "AS"]) {
51684            // Matched: EXECUTE AS
51685            return Ok(None);
51686        }
51687        Ok(None)
51688    }
51689
51690    /// parse_projections - Delegates to parse_expressions
51691    #[allow(unused_variables, unused_mut)]
51692    pub fn parse_projections(&mut self) -> Result<Option<Expression>> {
51693        self.parse_expressions()
51694    }
51695
51696    /// parse_properties - Parses table/column properties
51697    /// Python: _parse_properties
51698    /// Collects a list of properties using parse_property
51699    pub fn parse_properties(&mut self) -> Result<Option<Expression>> {
51700        self.parse_properties_impl(None)
51701    }
51702
51703    /// Implementation of parse_properties with before option
51704    pub fn parse_properties_impl(&mut self, before: Option<bool>) -> Result<Option<Expression>> {
51705        let mut properties = Vec::new();
51706
51707        loop {
51708            let prop = if before == Some(true) {
51709                self.parse_property_before()?
51710            } else {
51711                self.parse_property()?
51712            };
51713
51714            if let Some(p) = prop {
51715                properties.push(p);
51716            } else {
51717                break;
51718            }
51719        }
51720
51721        if properties.is_empty() {
51722            Ok(None)
51723        } else {
51724            Ok(Some(Expression::Properties(Box::new(Properties {
51725                expressions: properties,
51726            }))))
51727        }
51728    }
51729
51730    /// parse_property - Implemented from Python _parse_property
51731    /// Calls: parse_bitwise, parse_column, parse_sequence_properties
51732    #[allow(unused_variables, unused_mut)]
51733    pub fn parse_property(&mut self) -> Result<Option<Expression>> {
51734        if self.match_text_seq(&["COMPOUND", "SORTKEY"]) {
51735            return Ok(Some(Expression::Identifier(Identifier {
51736                name: String::new(),
51737                quoted: false,
51738                trailing_comments: Vec::new(),
51739                span: None,
51740            })));
51741        }
51742        if self.match_text_seq(&["SQL", "SECURITY"]) {
51743            // Matched: SQL SECURITY
51744            return Ok(None);
51745        }
51746        if self.match_texts(&["DEFINER", "INVOKER"]) {
51747            // Matched one of: DEFINER, INVOKER
51748            return Ok(None);
51749        }
51750        Ok(None)
51751    }
51752
51753    /// parse_on_cluster_clause - Parse ClickHouse ON CLUSTER clause
51754    fn parse_on_cluster_clause(&mut self) -> Result<Option<OnCluster>> {
51755        if !matches!(
51756            self.config.dialect,
51757            Some(crate::dialects::DialectType::ClickHouse)
51758        ) {
51759            return Ok(None);
51760        }
51761
51762        let start = self.current;
51763        if !self.match_token(TokenType::On) {
51764            return Ok(None);
51765        }
51766
51767        if !self.match_token(TokenType::Cluster) {
51768            self.current = start;
51769            return Ok(None);
51770        }
51771
51772        let this = if self.check(TokenType::String) {
51773            let value = self.expect_string()?;
51774            Expression::Literal(Box::new(Literal::String(value)))
51775        } else if let Some(id_expr) = self.parse_id_var()? {
51776            id_expr
51777        } else if self.is_safe_keyword_as_identifier() {
51778            let name = self.advance().text;
51779            Expression::Identifier(Identifier {
51780                name,
51781                quoted: false,
51782                trailing_comments: Vec::new(),
51783                span: None,
51784            })
51785        } else {
51786            return Err(self.parse_error("Expected cluster name after ON CLUSTER"));
51787        };
51788
51789        Ok(Some(OnCluster {
51790            this: Box::new(this),
51791        }))
51792    }
51793
51794    /// parse_clickhouse_table_properties - Parse ClickHouse table properties after column defs
51795    fn parse_clickhouse_table_properties(
51796        &mut self,
51797        properties: &mut Vec<Expression>,
51798    ) -> Result<()> {
51799        loop {
51800            if self.match_identifier("ENGINE") {
51801                self.match_token(TokenType::Eq);
51802                let engine = self.parse_clickhouse_engine_expression()?;
51803                properties.push(Expression::EngineProperty(Box::new(EngineProperty {
51804                    this: Box::new(engine),
51805                })));
51806                continue;
51807            }
51808
51809            if self.match_token(TokenType::Order) {
51810                self.expect(TokenType::By)?;
51811                let order_by = if matches!(
51812                    self.config.dialect,
51813                    Some(crate::dialects::DialectType::ClickHouse)
51814                ) && self.match_token(TokenType::LParen)
51815                {
51816                    // ClickHouse: ORDER BY (col1 [ASC|DESC], col2 [ASC|DESC], ...)
51817                    // or ORDER BY () for no ordering
51818                    if self.check(TokenType::RParen) {
51819                        self.skip();
51820                        OrderBy {
51821                            expressions: vec![Ordered::asc(Expression::Tuple(Box::new(Tuple {
51822                                expressions: Vec::new(),
51823                            })))],
51824                            siblings: false,
51825                            comments: Vec::new(),
51826                        }
51827                    } else {
51828                        // Parse all expressions inside the parentheses
51829                        let mut inner_exprs = Vec::new();
51830                        loop {
51831                            let expr = self.parse_expression()?;
51832                            inner_exprs.push(expr);
51833                            if !self.match_token(TokenType::Comma) {
51834                                break;
51835                            }
51836                        }
51837                        self.expect(TokenType::RParen)?;
51838                        // Wrap in a Tuple for multi-expr, Paren for single-expr
51839                        let wrapper = if inner_exprs.len() == 1 {
51840                            Expression::Paren(Box::new(Paren {
51841                                this: inner_exprs.into_iter().next().unwrap(),
51842                                trailing_comments: Vec::new(),
51843                            }))
51844                        } else {
51845                            Expression::Tuple(Box::new(Tuple {
51846                                expressions: inner_exprs,
51847                            }))
51848                        };
51849                        OrderBy {
51850                            expressions: vec![Ordered::asc(wrapper)],
51851                            siblings: false,
51852                            comments: Vec::new(),
51853                        }
51854                    }
51855                } else {
51856                    self.parse_order_by()?
51857                };
51858                properties.push(Expression::OrderBy(Box::new(order_by)));
51859                continue;
51860            }
51861
51862            if self.match_token(TokenType::Partition) {
51863                self.expect(TokenType::By)?;
51864                if self.check(TokenType::Order) && self.check_next(TokenType::By) {
51865                    return Err(self.parse_error("Expected expression after PARTITION BY"));
51866                }
51867                let expr = self
51868                    .parse_assignment()?
51869                    .ok_or_else(|| self.parse_error("Expected expression after PARTITION BY"))?;
51870                properties.push(Expression::PartitionedByProperty(Box::new(
51871                    PartitionedByProperty {
51872                        this: Box::new(expr),
51873                    },
51874                )));
51875                continue;
51876            }
51877
51878            if self.match_token(TokenType::PrimaryKey) {
51879                // ClickHouse supports PRIMARY KEY id and PRIMARY KEY (id, ...)
51880                let _ = self.match_token(TokenType::Key);
51881                if self.check(TokenType::LParen) {
51882                    if let Some(pk) = self.parse_primary_key_impl(false, true)? {
51883                        properties.push(pk);
51884                    }
51885                } else if let Some(expr) = self.parse_conjunction()? {
51886                    // ClickHouse: PRIMARY KEY expr (e.g., PRIMARY KEY tuple(), PRIMARY KEY id)
51887                    let mut exprs = vec![expr];
51888                    while self.match_token(TokenType::Comma) {
51889                        if let Some(next_expr) = self.parse_field()? {
51890                            exprs.push(next_expr);
51891                        } else {
51892                            break;
51893                        }
51894                    }
51895                    properties.push(Expression::PrimaryKey(Box::new(PrimaryKey {
51896                        this: None,
51897                        expressions: exprs,
51898                        options: Vec::new(),
51899                        include: None,
51900                    })));
51901                } else {
51902                    return Err(self.parse_error("Expected expression after PRIMARY KEY"));
51903                }
51904                continue;
51905            }
51906
51907            if self.match_token(TokenType::Sample) {
51908                let _ = self.match_token(TokenType::By);
51909                let expr = self.parse_expression()?;
51910                properties.push(Expression::SampleProperty(Box::new(SampleProperty {
51911                    this: Box::new(expr),
51912                })));
51913                continue;
51914            }
51915
51916            if self.match_token(TokenType::Settings) {
51917                let mut settings = Vec::new();
51918                loop {
51919                    settings.push(self.parse_expression()?);
51920                    if !self.match_token(TokenType::Comma) {
51921                        break;
51922                    }
51923                }
51924                properties.push(Expression::SettingsProperty(Box::new(SettingsProperty {
51925                    expressions: settings,
51926                })));
51927                continue;
51928            }
51929
51930            if self.match_token(TokenType::Comment) {
51931                let comment_expr = if self.check(TokenType::String) {
51932                    Expression::Literal(Box::new(Literal::String(self.expect_string()?)))
51933                } else {
51934                    self.parse_expression()?
51935                };
51936                properties.push(Expression::SchemaCommentProperty(Box::new(
51937                    SchemaCommentProperty {
51938                        this: Box::new(comment_expr),
51939                    },
51940                )));
51941                continue;
51942            }
51943
51944            // TTL time_column + INTERVAL '1' MONTH [DELETE|RECOMPRESS|TO DISK|TO VOLUME] [WHERE ...]
51945            if self.match_identifier("TTL") {
51946                if let Some(ttl_expr) = self.parse_ttl()? {
51947                    properties.push(ttl_expr);
51948                }
51949                continue;
51950            }
51951
51952            if self.match_identifier("SOURCE") {
51953                if let Some(prop) = self.parse_dict_property("SOURCE")? {
51954                    properties.push(prop);
51955                }
51956                continue;
51957            }
51958
51959            if self.match_identifier("LAYOUT") {
51960                if let Some(prop) = self.parse_dict_property("LAYOUT")? {
51961                    properties.push(prop);
51962                }
51963                continue;
51964            }
51965
51966            if self.match_identifier("LIFETIME") {
51967                if let Some(range) = self.parse_dict_range("LIFETIME")? {
51968                    properties.push(range);
51969                }
51970                continue;
51971            }
51972
51973            if self.match_identifier("RANGE") || self.match_token(TokenType::Range) {
51974                if let Some(range) = self.parse_dict_range("RANGE")? {
51975                    properties.push(range);
51976                }
51977                continue;
51978            }
51979
51980            break;
51981        }
51982
51983        Ok(())
51984    }
51985
51986    /// ClickHouse implicit alias in function arguments: `expr identifier` (without AS keyword).
51987    /// The token after the alias must be a delimiter (comma, RParen, FROM, FOR, AS).
51988    fn try_clickhouse_implicit_alias(&mut self, expr: Expression) -> Expression {
51989        if !matches!(
51990            self.config.dialect,
51991            Some(crate::dialects::DialectType::ClickHouse)
51992        ) {
51993            return expr;
51994        }
51995        if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
51996            let next_after = self.peek_nth(1).map(|t| t.token_type);
51997            let is_delimiter = matches!(
51998                next_after,
51999                Some(TokenType::Comma)
52000                    | Some(TokenType::RParen)
52001                    | Some(TokenType::From)
52002                    | Some(TokenType::For)
52003                    | Some(TokenType::As)
52004            );
52005            if is_delimiter {
52006                let alias_token = self.advance();
52007                let alias_name = alias_token.text.clone();
52008                return Expression::Alias(Box::new(crate::expressions::Alias::new(
52009                    expr,
52010                    Identifier::new(alias_name),
52011                )));
52012            }
52013        }
52014        expr
52015    }
52016
52017    /// ClickHouse alias in function arguments: handles both implicit (`expr identifier`)
52018    /// and explicit (`expr AS identifier`) aliases. Use this in special function parsers
52019    /// (SUBSTRING, TRIM, EXTRACT) but NOT in CAST (which has its own AS handling).
52020    /// Normalize TSQL date part aliases (e.g., dd -> DAY, yy -> YEAR, etc.)
52021    fn normalize_tsql_date_part(&self, expr: Expression) -> Expression {
52022        let name = match &expr {
52023            Expression::Var(v) => Some(v.this.to_ascii_uppercase()),
52024            Expression::Column(c) if c.table.is_none() => Some(c.name.name.to_ascii_uppercase()),
52025            Expression::Identifier(id) => Some(id.name.to_ascii_uppercase()),
52026            _ => None,
52027        };
52028        if let Some(name) = name {
52029            let mapped = match name.as_str() {
52030                "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
52031                "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
52032                "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
52033                "DOW" | "DW" | "WEEKDAY" => "DAYOFWEEK",
52034                "DOY" | "DY" | "Y" => "DAYOFYEAR",
52035                "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" => "WEEK",
52036                "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
52037                "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
52038                "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
52039                "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
52040                "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
52041                | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
52042                "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
52043                | "MICROSECONDS" | "MCS" => "MICROSECOND",
52044                "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
52045                "TZH" => "TIMEZONE_HOUR",
52046                "TZM" | "TZOFFSET" | "TZ" => "TIMEZONE_MINUTE",
52047                "DEC" | "DECS" | "DECADES" => "DECADE",
52048                "MIL" | "MILS" | "MILLENIA" => "MILLENNIUM",
52049                "C" | "CENT" | "CENTS" | "CENTURIES" => "CENTURY",
52050                "ISOWK" | "ISOWW" | "ISO_WEEK" | "WEEKOFYEARISO" | "WEEKOFYEAR_ISO"
52051                | "WEEK_ISO" => "WEEKISO",
52052                _ => return expr, // No mapping, return as-is
52053            };
52054            return Expression::Var(Box::new(Var {
52055                this: mapped.to_string(),
52056            }));
52057        }
52058        expr
52059    }
52060
52061    fn try_parse_date_part_unit_expr(&self, expr: &Expression) -> Option<IntervalUnit> {
52062        let upper = self.date_part_expr_name(expr)?.to_ascii_uppercase();
52063        let canonical = match upper.as_str() {
52064            // Year
52065            "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
52066            // Quarter
52067            "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
52068            // Month
52069            "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
52070            // Week
52071            "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" | "WEEKS" => "WEEK",
52072            // Day
52073            "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
52074            // Hour
52075            "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
52076            // Minute
52077            "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
52078            // Second
52079            "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
52080            // Millisecond
52081            "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
52082            | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
52083            // Microsecond
52084            "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
52085            | "MICROSECONDS" | "MCS" => "MICROSECOND",
52086            // Nanosecond
52087            "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
52088            _ => upper.as_str(),
52089        };
52090
52091        Self::parse_interval_unit_from_string(canonical)
52092    }
52093
52094    fn try_parse_date_part_unit_identifier_expr(&self, expr: &Expression) -> Option<IntervalUnit> {
52095        let upper = self
52096            .date_part_identifier_expr_name(expr)?
52097            .to_ascii_uppercase();
52098        let canonical = match upper.as_str() {
52099            "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => "YEAR",
52100            "Q" | "QTR" | "QTRS" | "QUARTERS" | "QQ" => "QUARTER",
52101            "MM" | "MON" | "MONS" | "MONTHS" | "M" => "MONTH",
52102            "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WY" | "WW" | "WEEKS" => "WEEK",
52103            "D" | "DD" | "DAYS" | "DAYOFMONTH" => "DAY",
52104            "H" | "HH" | "HR" | "HOURS" | "HRS" => "HOUR",
52105            "MI" | "MIN" | "MINUTES" | "MINS" | "N" => "MINUTE",
52106            "S" | "SEC" | "SECONDS" | "SECS" | "SS" => "SECOND",
52107            "MS" | "MSEC" | "MSECS" | "MSECOND" | "MSECONDS" | "MILLISEC" | "MILLISECS"
52108            | "MILLISECON" | "MILLISECONDS" => "MILLISECOND",
52109            "US" | "USEC" | "USECS" | "MICROSEC" | "MICROSECS" | "USECOND" | "USECONDS"
52110            | "MICROSECONDS" | "MCS" => "MICROSECOND",
52111            "NS" | "NSEC" | "NANOSEC" | "NSECOND" | "NSECONDS" | "NANOSECS" => "NANOSECOND",
52112            _ => upper.as_str(),
52113        };
52114
52115        Self::parse_interval_unit_from_string(canonical)
52116    }
52117
52118    fn try_parse_date_part_field_identifier_expr(
52119        &self,
52120        expr: &Expression,
52121    ) -> Option<DateTimeField> {
52122        let upper = self
52123            .date_part_identifier_expr_name(expr)?
52124            .to_ascii_uppercase();
52125        Some(match upper.as_str() {
52126            "YEAR" | "Y" | "YY" | "YYY" | "YYYY" | "YR" | "YEARS" | "YRS" => DateTimeField::Year,
52127            "MONTH" | "MM" | "MON" | "MONS" | "MONTHS" => DateTimeField::Month,
52128            "DAY" | "D" | "DD" | "DAYS" | "DAYOFMONTH" => DateTimeField::Day,
52129            "HOUR" | "H" | "HH" | "HR" | "HOURS" | "HRS" => DateTimeField::Hour,
52130            "MINUTE" | "MI" | "MIN" | "MINUTES" | "MINS" => DateTimeField::Minute,
52131            "SECOND" | "S" | "SEC" | "SECONDS" | "SECS" => DateTimeField::Second,
52132            "MILLISECOND" | "MS" | "MSEC" | "MILLISECONDS" => DateTimeField::Millisecond,
52133            "MICROSECOND" | "US" | "USEC" | "MICROSECONDS" => DateTimeField::Microsecond,
52134            "DOW" | "DAYOFWEEK" | "DW" => DateTimeField::DayOfWeek,
52135            "DOY" | "DAYOFYEAR" | "DY" => DateTimeField::DayOfYear,
52136            "WEEK" | "W" | "WK" | "WEEKOFYEAR" | "WOY" | "WW" => DateTimeField::Week,
52137            "QUARTER" | "Q" | "QTR" | "QTRS" | "QUARTERS" => DateTimeField::Quarter,
52138            "EPOCH" | "EPOCH_SECOND" | "EPOCH_SECONDS" => DateTimeField::Epoch,
52139            "TIMEZONE" => DateTimeField::Timezone,
52140            "TIMEZONE_HOUR" | "TZH" => DateTimeField::TimezoneHour,
52141            "TIMEZONE_MINUTE" | "TZM" => DateTimeField::TimezoneMinute,
52142            "DATE" => DateTimeField::Date,
52143            "TIME" => DateTimeField::Time,
52144            other => DateTimeField::Custom(other.to_string()),
52145        })
52146    }
52147
52148    fn convert_date_part_identifier_expr_to_var(&self, expr: Expression) -> Expression {
52149        match expr {
52150            Expression::Var(_) => expr,
52151            Expression::Column(c) if c.table.is_none() => {
52152                Expression::Var(Box::new(Var { this: c.name.name }))
52153            }
52154            Expression::Identifier(id) => Expression::Var(Box::new(Var { this: id.name })),
52155            _ => expr,
52156        }
52157    }
52158
52159    fn date_part_identifier_expr_name<'a>(&self, expr: &'a Expression) -> Option<&'a str> {
52160        match expr {
52161            Expression::Var(v) => Some(v.this.as_str()),
52162            Expression::Column(c) if c.table.is_none() => Some(c.name.name.as_str()),
52163            Expression::Identifier(id) => Some(id.name.as_str()),
52164            _ => None,
52165        }
52166    }
52167
52168    fn date_part_expr_name<'a>(&self, expr: &'a Expression) -> Option<&'a str> {
52169        self.date_part_identifier_expr_name(expr).or(match expr {
52170            Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
52171                let Literal::String(s) = lit.as_ref() else {
52172                    unreachable!()
52173                };
52174                Some(s.as_str())
52175            }
52176            _ => None,
52177        })
52178    }
52179
52180    fn try_clickhouse_func_arg_alias(&mut self, expr: Expression) -> Expression {
52181        if !matches!(
52182            self.config.dialect,
52183            Some(crate::dialects::DialectType::ClickHouse)
52184        ) {
52185            return expr;
52186        }
52187        // Try implicit alias first
52188        if self.check(TokenType::Var) || self.check(TokenType::Identifier) {
52189            let next_after = self.peek_nth(1).map(|t| t.token_type);
52190            let is_delimiter = matches!(
52191                next_after,
52192                Some(TokenType::Comma)
52193                    | Some(TokenType::RParen)
52194                    | Some(TokenType::From)
52195                    | Some(TokenType::For)
52196                    | Some(TokenType::As)
52197            );
52198            if is_delimiter {
52199                let alias_token = self.advance();
52200                let alias_name = alias_token.text.clone();
52201                return Expression::Alias(Box::new(crate::expressions::Alias::new(
52202                    expr,
52203                    Identifier::new(alias_name),
52204                )));
52205            }
52206        }
52207        // Try explicit AS alias
52208        if self.check(TokenType::As) {
52209            let next_idx = self.current + 1;
52210            let after_alias_idx = self.current + 2;
52211            let is_alias_token = next_idx < self.tokens.len()
52212                && matches!(
52213                    self.tokens[next_idx].token_type,
52214                    TokenType::Identifier | TokenType::Var | TokenType::QuotedIdentifier
52215                );
52216            let is_delimiter = is_alias_token
52217                && after_alias_idx < self.tokens.len()
52218                && matches!(
52219                    self.tokens[after_alias_idx].token_type,
52220                    TokenType::Comma
52221                        | TokenType::RParen
52222                        | TokenType::From
52223                        | TokenType::For
52224                        | TokenType::As
52225                );
52226            if is_delimiter {
52227                self.skip(); // consume AS
52228                let alias_token = self.advance();
52229                let alias_name = if alias_token.token_type == TokenType::QuotedIdentifier {
52230                    let mut ident = Identifier::new(alias_token.text.clone());
52231                    ident.quoted = true;
52232                    ident
52233                } else {
52234                    Identifier::new(alias_token.text.clone())
52235                };
52236                return Expression::Alias(Box::new(crate::expressions::Alias::new(
52237                    expr, alias_name,
52238                )));
52239            }
52240        }
52241        expr
52242    }
52243
52244    /// parse_clickhouse_engine_expression - Parse ENGINE expression with optional args
52245    fn parse_clickhouse_engine_expression(&mut self) -> Result<Expression> {
52246        if self.is_at_end() {
52247            return Err(self.parse_error("Expected engine name after ENGINE"));
52248        }
52249
52250        let token = self.advance();
52251        let quoted = matches!(token.token_type, TokenType::QuotedIdentifier);
52252        let name = token.text.clone();
52253
52254        let ident = Expression::Identifier(Identifier {
52255            name,
52256            quoted,
52257            trailing_comments: Vec::new(),
52258            span: None,
52259        });
52260
52261        if self.match_token(TokenType::LParen) {
52262            let args = if self.check(TokenType::RParen) {
52263                Vec::new()
52264            } else {
52265                self.parse_expression_list()?
52266            };
52267            self.expect(TokenType::RParen)?;
52268            Ok(Expression::Anonymous(Box::new(Anonymous {
52269                this: Box::new(ident),
52270                expressions: args,
52271            })))
52272        } else {
52273            Ok(ident)
52274        }
52275    }
52276
52277    /// parse_property_assignment - Ported from Python _parse_property_assignment
52278    /// Parses a property assignment: optionally = or AS, then a value
52279    #[allow(unused_variables, unused_mut)]
52280    pub fn parse_property_assignment(&mut self) -> Result<Option<Expression>> {
52281        // Optionally match = or AS
52282        let _ = self.match_token(TokenType::Eq);
52283        let _ = self.match_token(TokenType::Alias);
52284
52285        // Parse the value as an unquoted field
52286        let value = self.parse_unquoted_field()?;
52287
52288        Ok(value)
52289    }
52290
52291    /// parse_property_before - Implemented from Python _parse_property_before
52292    #[allow(unused_variables, unused_mut)]
52293    pub fn parse_property_before(&mut self) -> Result<Option<Expression>> {
52294        if self.match_text_seq(&["NO"]) {
52295            // Matched: NO
52296            return Ok(None);
52297        }
52298        if self.match_text_seq(&["DUAL"]) {
52299            // Matched: DUAL
52300            return Ok(None);
52301        }
52302        if self.match_text_seq(&["BEFORE"]) {
52303            // Matched: BEFORE
52304            return Ok(None);
52305        }
52306        if self.match_texts(&["MIN", "MINIMUM"]) {
52307            // Matched one of: MIN, MINIMUM
52308            return Ok(None);
52309        }
52310        if self.match_texts(&["MAX", "MAXIMUM"]) {
52311            // Matched one of: MAX, MAXIMUM
52312            return Ok(None);
52313        }
52314        Ok(None)
52315    }
52316
52317    /// parse_qualify - Parse QUALIFY clause (Snowflake, BigQuery)
52318    /// Python: if not self._match(TokenType.QUALIFY): return None; return exp.Qualify(this=self._parse_disjunction())
52319    pub fn parse_qualify(&mut self) -> Result<Option<Expression>> {
52320        if !self.match_token(TokenType::Qualify) {
52321            return Ok(None);
52322        }
52323        let condition = self.parse_expression()?;
52324        Ok(Some(Expression::Qualify(Box::new(Qualify {
52325            this: condition,
52326        }))))
52327    }
52328
52329    /// parse_range - Parses range expressions (BETWEEN, LIKE, IN, IS, etc.)
52330    /// Python: _parse_range
52331    pub fn parse_range(&mut self) -> Result<Option<Expression>> {
52332        // First parse a bitwise expression as the left side
52333        let mut this = self.parse_bitwise()?;
52334        if this.is_none() {
52335            return Ok(None);
52336        }
52337
52338        // Check for NOT (for NOT LIKE, NOT IN, NOT BETWEEN, etc.)
52339        let negate = self.match_token(TokenType::Not);
52340
52341        // BETWEEN
52342        if self.match_token(TokenType::Between) {
52343            let between = self.parse_between_with_expr(this.clone(), negate)?;
52344            this = Some(between);
52345            return Ok(this);
52346        }
52347
52348        // LIKE
52349        if self.match_token(TokenType::Like) {
52350            let left = this.clone().expect("left expression checked above");
52351            let right = self
52352                .parse_bitwise()?
52353                .ok_or_else(|| self.parse_error("Expected expression after LIKE"))?;
52354            let escape = self.parse_escape()?;
52355            let like = Expression::Like(Box::new(LikeOp {
52356                left,
52357                right,
52358                escape,
52359                quantifier: None,
52360                inferred_type: None,
52361            }));
52362            this = if negate {
52363                Some(Expression::Not(Box::new(UnaryOp {
52364                    this: like,
52365                    inferred_type: None,
52366                })))
52367            } else {
52368                Some(like)
52369            };
52370            return Ok(this);
52371        }
52372
52373        // ILIKE
52374        if self.match_token(TokenType::ILike) {
52375            let left = this.clone().expect("left expression checked above");
52376            let right = self
52377                .parse_bitwise()?
52378                .ok_or_else(|| self.parse_error("Expected expression after ILIKE"))?;
52379            let escape = self.parse_escape()?;
52380            let ilike = Expression::ILike(Box::new(LikeOp {
52381                left,
52382                right,
52383                escape,
52384                quantifier: None,
52385                inferred_type: None,
52386            }));
52387            this = if negate {
52388                Some(Expression::Not(Box::new(UnaryOp {
52389                    this: ilike,
52390                    inferred_type: None,
52391                })))
52392            } else {
52393                Some(ilike)
52394            };
52395            return Ok(this);
52396        }
52397
52398        // IN
52399        if self.match_token(TokenType::In) {
52400            let in_expr = self.parse_in_with_expr(this.clone())?;
52401            this = if negate {
52402                Some(Expression::Not(Box::new(UnaryOp {
52403                    this: in_expr,
52404                    inferred_type: None,
52405                })))
52406            } else {
52407                Some(in_expr)
52408            };
52409            return Ok(this);
52410        }
52411
52412        // IS [NOT] NULL / IS [NOT] TRUE / IS [NOT] FALSE
52413        if self.match_token(TokenType::Is) {
52414            let is_expr = self.parse_is_with_expr(this.clone())?;
52415            this = Some(is_expr);
52416            return Ok(this);
52417        }
52418
52419        // Handle standalone NOT with NULL (for NOT NULL pattern after negate)
52420        if negate && self.match_token(TokenType::Null) {
52421            if let Some(left) = this {
52422                let is_null = Expression::Is(Box::new(BinaryOp {
52423                    left,
52424                    right: Expression::Null(Null),
52425                    left_comments: Vec::new(),
52426                    operator_comments: Vec::new(),
52427                    trailing_comments: Vec::new(),
52428                    inferred_type: None,
52429                }));
52430                return Ok(Some(Expression::Not(Box::new(UnaryOp {
52431                    this: is_null,
52432                    inferred_type: None,
52433                }))));
52434            }
52435        }
52436
52437        Ok(this)
52438    }
52439
52440    /// parse_between_with_expr - Parses BETWEEN expression with given left side
52441    fn parse_between_with_expr(
52442        &mut self,
52443        this: Option<Expression>,
52444        negate: bool,
52445    ) -> Result<Expression> {
52446        let this_expr = match this {
52447            Some(e) => e,
52448            None => return Err(self.parse_error("Expected expression before BETWEEN")),
52449        };
52450
52451        // Check for SYMMETRIC/ASYMMETRIC qualifier
52452        let symmetric = if self.match_texts(&["SYMMETRIC"]) {
52453            Some(true)
52454        } else if self.match_texts(&["ASYMMETRIC"]) {
52455            Some(false)
52456        } else {
52457            None
52458        };
52459
52460        let low = self
52461            .parse_bitwise()?
52462            .ok_or_else(|| self.parse_error("Expected low expression after BETWEEN"))?;
52463
52464        if !self.match_token(TokenType::And) {
52465            return Err(self.parse_error("Expected AND in BETWEEN expression"));
52466        }
52467
52468        let high = self
52469            .parse_bitwise()?
52470            .ok_or_else(|| self.parse_error("Expected high expression after AND in BETWEEN"))?;
52471
52472        Ok(Expression::Between(Box::new(Between {
52473            this: this_expr,
52474            low,
52475            high,
52476            not: negate,
52477            symmetric,
52478        })))
52479    }
52480
52481    /// parse_in_with_expr - Parses IN expression with given left side
52482    fn parse_in_with_expr(&mut self, this: Option<Expression>) -> Result<Expression> {
52483        let this_expr = match this {
52484            Some(e) => e,
52485            None => return Err(self.parse_error("Expected expression before IN")),
52486        };
52487
52488        // BigQuery: IN UNNEST(expr) — UNNEST without wrapping parentheses
52489        if self.check_identifier("UNNEST") {
52490            self.skip(); // consume UNNEST
52491            self.expect(TokenType::LParen)?;
52492            let unnest_expr = self.parse_expression()?;
52493            self.expect(TokenType::RParen)?;
52494            return Ok(Expression::In(Box::new(In {
52495                this: this_expr,
52496                expressions: Vec::new(),
52497                query: None,
52498                not: false,
52499                global: false,
52500                unnest: Some(Box::new(unnest_expr)),
52501                is_field: false,
52502            })));
52503        }
52504
52505        // Parse the IN list (subquery or value list)
52506        if !self.match_token(TokenType::LParen) {
52507            // DuckDB: IN without parentheses for array/list membership: 'red' IN tbl.flags
52508            // Try to parse as a single expression (column/array reference)
52509            if let Ok(expr) = self.parse_primary() {
52510                return Ok(Expression::In(Box::new(In {
52511                    this: this_expr,
52512                    expressions: vec![expr],
52513                    query: None,
52514                    not: false,
52515                    global: false,
52516                    unnest: None,
52517                    is_field: true,
52518                })));
52519            }
52520            return Err(self.parse_error("Expected expression or parenthesized list after IN"));
52521        }
52522
52523        // Check if it's a subquery
52524        if self.check(TokenType::Select) {
52525            let subquery = self.parse_select()?;
52526            self.expect(TokenType::RParen)?;
52527            return Ok(Expression::In(Box::new(In {
52528                this: this_expr,
52529                expressions: Vec::new(),
52530                query: Some(subquery),
52531                not: false,
52532                global: false,
52533                unnest: None,
52534                is_field: false,
52535            })));
52536        }
52537
52538        // Parse value list. Pre-size for large IN lists to reduce reallocations.
52539        let capacity_hint = self.estimate_expression_list_capacity_until_rparen();
52540        let expressions = self.parse_expression_list_with_capacity(capacity_hint)?;
52541        self.expect(TokenType::RParen)?;
52542
52543        if expressions.is_empty() {
52544            return Err(self.parse_error("Expected expression list after IN"));
52545        }
52546
52547        Ok(Expression::In(Box::new(In {
52548            this: this_expr,
52549            expressions,
52550            query: None,
52551            not: false,
52552            global: false,
52553            unnest: None,
52554            is_field: false,
52555        })))
52556    }
52557
52558    /// parse_is_with_expr - Parses IS expression with given left side
52559    fn parse_is_with_expr(&mut self, this: Option<Expression>) -> Result<Expression> {
52560        let this_expr = match this {
52561            Some(e) => e,
52562            None => return Err(self.parse_error("Expected expression before IS")),
52563        };
52564
52565        let negate = self.match_token(TokenType::Not);
52566
52567        // IS NULL
52568        if self.match_token(TokenType::Null) {
52569            let is_null = Expression::Is(Box::new(BinaryOp {
52570                left: this_expr,
52571                right: Expression::Null(Null),
52572                left_comments: Vec::new(),
52573                operator_comments: Vec::new(),
52574                trailing_comments: Vec::new(),
52575                inferred_type: None,
52576            }));
52577            return if negate {
52578                Ok(Expression::Not(Box::new(UnaryOp {
52579                    this: is_null,
52580                    inferred_type: None,
52581                })))
52582            } else {
52583                Ok(is_null)
52584            };
52585        }
52586
52587        // IS TRUE
52588        if self.match_texts(&["TRUE"]) {
52589            let is_true = Expression::Is(Box::new(BinaryOp {
52590                left: this_expr,
52591                right: Expression::Boolean(BooleanLiteral { value: true }),
52592                left_comments: Vec::new(),
52593                operator_comments: Vec::new(),
52594                trailing_comments: Vec::new(),
52595                inferred_type: None,
52596            }));
52597            return if negate {
52598                Ok(Expression::Not(Box::new(UnaryOp {
52599                    this: is_true,
52600                    inferred_type: None,
52601                })))
52602            } else {
52603                Ok(is_true)
52604            };
52605        }
52606
52607        // IS FALSE
52608        if self.match_texts(&["FALSE"]) {
52609            let is_false = Expression::Is(Box::new(BinaryOp {
52610                left: this_expr,
52611                right: Expression::Boolean(BooleanLiteral { value: false }),
52612                left_comments: Vec::new(),
52613                operator_comments: Vec::new(),
52614                trailing_comments: Vec::new(),
52615                inferred_type: None,
52616            }));
52617            return if negate {
52618                Ok(Expression::Not(Box::new(UnaryOp {
52619                    this: is_false,
52620                    inferred_type: None,
52621                })))
52622            } else {
52623                Ok(is_false)
52624            };
52625        }
52626
52627        // IS JSON [VALUE|SCALAR|OBJECT|ARRAY] [WITH UNIQUE KEYS|WITHOUT UNIQUE KEYS|UNIQUE KEYS]
52628        if self.match_texts(&["JSON"]) {
52629            // Parse optional JSON type
52630            let json_type = if self.match_texts(&["VALUE"]) {
52631                Some("VALUE".to_string())
52632            } else if self.match_texts(&["SCALAR"]) {
52633                Some("SCALAR".to_string())
52634            } else if self.match_texts(&["OBJECT"]) {
52635                Some("OBJECT".to_string())
52636            } else if self.match_texts(&["ARRAY"]) {
52637                Some("ARRAY".to_string())
52638            } else {
52639                None
52640            };
52641
52642            // Parse optional key uniqueness constraint
52643            let unique_keys = if self.match_text_seq(&["WITH", "UNIQUE", "KEYS"]) {
52644                Some(JsonUniqueKeys::With)
52645            } else if self.match_text_seq(&["WITHOUT", "UNIQUE", "KEYS"]) {
52646                Some(JsonUniqueKeys::Without)
52647            } else if self.match_text_seq(&["UNIQUE", "KEYS"]) {
52648                // Shorthand for WITH UNIQUE KEYS
52649                Some(JsonUniqueKeys::Shorthand)
52650            } else {
52651                None
52652            };
52653
52654            return Ok(Expression::IsJson(Box::new(IsJson {
52655                this: this_expr,
52656                json_type,
52657                unique_keys,
52658                negated: negate,
52659            })));
52660        }
52661
52662        // IS DISTINCT FROM / IS NOT DISTINCT FROM
52663        if self.match_text_seq(&["DISTINCT", "FROM"]) {
52664            let right = self.parse_bitwise()?;
52665            if let Some(right_expr) = right {
52666                // IS DISTINCT FROM is semantically "not equal with null handling"
52667                // Use NullSafeNeq for IS DISTINCT FROM
52668                // If negate was set (IS NOT DISTINCT FROM), use NullSafeEq
52669                let expr = if negate {
52670                    Expression::NullSafeEq(Box::new(BinaryOp {
52671                        left: this_expr,
52672                        right: right_expr,
52673                        left_comments: Vec::new(),
52674                        operator_comments: Vec::new(),
52675                        trailing_comments: Vec::new(),
52676                        inferred_type: None,
52677                    }))
52678                } else {
52679                    Expression::NullSafeNeq(Box::new(BinaryOp {
52680                        left: this_expr,
52681                        right: right_expr,
52682                        left_comments: Vec::new(),
52683                        operator_comments: Vec::new(),
52684                        trailing_comments: Vec::new(),
52685                        inferred_type: None,
52686                    }))
52687                };
52688                return Ok(expr);
52689            }
52690            return Err(self.parse_error("Expected expression after IS DISTINCT FROM"));
52691        }
52692
52693        Err(self.parse_error("Expected NULL, TRUE, FALSE, JSON, or DISTINCT FROM after IS"))
52694    }
52695
52696    /// parse_reads_property - Implemented from Python _parse_reads_property
52697    #[allow(unused_variables, unused_mut)]
52698    pub fn parse_reads_property(&mut self) -> Result<Option<Expression>> {
52699        if self.match_text_seq(&["SQL", "DATA"]) {
52700            // Matched: SQL DATA
52701            return Ok(None);
52702        }
52703        Ok(None)
52704    }
52705
52706    /// parse_recursive_with_search - Parse SEARCH/CYCLE clause for recursive CTEs (PostgreSQL)
52707    /// Syntax: SEARCH BREADTH|DEPTH FIRST BY column SET column [USING column]
52708    ///     or: CYCLE column SET column USING column
52709    #[allow(unused_variables, unused_mut)]
52710    pub fn parse_recursive_with_search(&mut self) -> Result<Option<Box<Expression>>> {
52711        // Check for SEARCH or CYCLE keyword
52712        let kind = if self.match_text_seq(&["SEARCH"]) {
52713            // SEARCH BREADTH|DEPTH FIRST BY ...
52714            let search_kind = if self.match_text_seq(&["BREADTH"]) {
52715                "BREADTH"
52716            } else if self.match_text_seq(&["DEPTH"]) {
52717                "DEPTH"
52718            } else {
52719                return Ok(None);
52720            };
52721            // Consume "FIRST BY"
52722            self.match_text_seq(&["FIRST"]);
52723            self.match_text_seq(&["BY"]);
52724            search_kind.to_string()
52725        } else if self.match_token(TokenType::Cycle) {
52726            "CYCLE".to_string()
52727        } else {
52728            return Ok(None);
52729        };
52730
52731        // Parse the column(s) - for CYCLE this is typically a single column
52732        let this = self.expect_identifier()?;
52733        let this_expr = Expression::Identifier(Identifier::new(this));
52734
52735        // SET column
52736        let expression = if self.match_text_seq(&["SET"]) {
52737            let set_col = self.expect_identifier()?;
52738            Expression::Identifier(Identifier::new(set_col))
52739        } else {
52740            return Err(self.parse_error("Expected SET in CYCLE/SEARCH clause"));
52741        };
52742
52743        // USING column (optional for SEARCH, required for CYCLE)
52744        let using = if self.match_token(TokenType::Using) {
52745            let using_col = self.expect_identifier()?;
52746            Some(Box::new(Expression::Identifier(Identifier::new(using_col))))
52747        } else {
52748            None
52749        };
52750
52751        Ok(Some(Box::new(Expression::RecursiveWithSearch(Box::new(
52752            RecursiveWithSearch {
52753                kind,
52754                this: Box::new(this_expr),
52755                expression: Box::new(expression),
52756                using,
52757            },
52758        )))))
52759    }
52760
52761    /// parse_references - Ported from Python _parse_references
52762    /// Parses REFERENCES clause for foreign key constraints
52763    #[allow(unused_variables, unused_mut)]
52764    pub fn parse_references(&mut self) -> Result<Option<Expression>> {
52765        if !self.match_token(TokenType::References) {
52766            return Ok(None);
52767        }
52768
52769        // Parse referenced table
52770        let this = self.parse_table()?;
52771        if this.is_none() {
52772            return Err(self.parse_error("Expected table name after REFERENCES"));
52773        }
52774
52775        // Parse optional column list (table(col1, col2))
52776        let expressions = if self.match_token(TokenType::LParen) {
52777            let cols = self.parse_identifier_list()?;
52778            self.expect(TokenType::RParen)?;
52779            cols.into_iter()
52780                .map(|id| Expression::Identifier(id))
52781                .collect()
52782        } else {
52783            Vec::new()
52784        };
52785
52786        // Parse optional constraint options (ON DELETE, ON UPDATE, etc.)
52787        let options = self.parse_fk_constraint_options()?;
52788
52789        Ok(Some(Expression::Reference(Box::new(Reference {
52790            this: Box::new(this.unwrap()),
52791            expressions,
52792            options,
52793        }))))
52794    }
52795
52796    /// Parse key constraint options (ON DELETE CASCADE, ON UPDATE SET NULL, etc.)
52797    fn parse_fk_constraint_options(&mut self) -> Result<Vec<Expression>> {
52798        let mut options = Vec::new();
52799
52800        while self.match_token(TokenType::On) {
52801            let kind = if self.match_token(TokenType::Delete) {
52802                "DELETE"
52803            } else if self.match_token(TokenType::Update) {
52804                "UPDATE"
52805            } else {
52806                break;
52807            };
52808
52809            let action = if self.match_text_seq(&["NO", "ACTION"]) {
52810                "NO ACTION"
52811            } else if self.match_text_seq(&["SET", "NULL"]) {
52812                "SET NULL"
52813            } else if self.match_text_seq(&["SET", "DEFAULT"]) {
52814                "SET DEFAULT"
52815            } else if self.match_token(TokenType::Cascade) {
52816                "CASCADE"
52817            } else if self.match_token(TokenType::Restrict) {
52818                "RESTRICT"
52819            } else {
52820                continue;
52821            };
52822
52823            // Store as simple identifier with the full action description
52824            options.push(Expression::Identifier(Identifier {
52825                name: format!("ON {} {}", kind, action),
52826                quoted: false,
52827                trailing_comments: Vec::new(),
52828                span: None,
52829            }));
52830        }
52831
52832        // Parse MATCH option
52833        if self.match_token(TokenType::Match) {
52834            let match_type = if self.match_identifier("FULL") {
52835                "FULL"
52836            } else if self.match_identifier("PARTIAL") {
52837                "PARTIAL"
52838            } else if self.match_identifier("SIMPLE") {
52839                "SIMPLE"
52840            } else {
52841                ""
52842            };
52843            if !match_type.is_empty() {
52844                options.push(Expression::Identifier(Identifier {
52845                    name: format!("MATCH {}", match_type),
52846                    quoted: false,
52847                    trailing_comments: Vec::new(),
52848                    span: None,
52849                }));
52850            }
52851        }
52852
52853        Ok(options)
52854    }
52855
52856    /// parse_refresh - Implemented from Python _parse_refresh
52857    #[allow(unused_variables, unused_mut)]
52858    /// parse_refresh - Parses REFRESH TABLE or REFRESH MATERIALIZED VIEW
52859    /// Python: parser.py:7656-7668
52860    pub fn parse_refresh(&mut self) -> Result<Option<Expression>> {
52861        let kind = if self.match_token(TokenType::Table) {
52862            "TABLE".to_string()
52863        } else if self.match_text_seq(&["MATERIALIZED", "VIEW"]) {
52864            "MATERIALIZED VIEW".to_string()
52865        } else {
52866            String::new()
52867        };
52868
52869        // Parse the object name (string literal or table name)
52870        // First try a string literal, then fall back to table reference
52871        if let Some(s) = self.parse_string()? {
52872            return Ok(Some(Expression::Refresh(Box::new(Refresh {
52873                this: Box::new(s),
52874                kind,
52875            }))));
52876        }
52877
52878        // Parse as a table reference (schema.table format)
52879        let table_ref = self.parse_table_ref()?;
52880        let table_expr = Expression::Table(Box::new(table_ref));
52881
52882        Ok(Some(Expression::Refresh(Box::new(Refresh {
52883            this: Box::new(table_expr),
52884            kind,
52885        }))))
52886    }
52887
52888    /// parse_refresh_trigger_property - Doris REFRESH clause for materialized views
52889    /// Syntax: REFRESH method ON kind [EVERY n UNIT] [STARTS 'datetime']
52890    /// Examples:
52891    ///   REFRESH COMPLETE ON MANUAL
52892    ///   REFRESH AUTO ON COMMIT
52893    ///   REFRESH AUTO ON SCHEDULE EVERY 5 MINUTE STARTS '2025-01-01 00:00:00'
52894    pub fn parse_refresh_trigger_property(&mut self) -> Result<RefreshTriggerProperty> {
52895        // Parse method: COMPLETE or AUTO
52896        let method = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
52897
52898        // Parse ON
52899        self.expect(TokenType::On)?;
52900
52901        // Parse kind: MANUAL, COMMIT, or SCHEDULE
52902        let kind_text = self.expect_identifier_or_keyword()?.to_ascii_uppercase();
52903        let kind = Some(kind_text.clone());
52904
52905        // For SCHEDULE, parse EVERY n UNIT [STARTS 'datetime']
52906        let (every, unit, starts) = if kind_text == "SCHEDULE" {
52907            // EVERY n UNIT
52908            let every = if self.match_identifier("EVERY") {
52909                // parse_number returns Option<Expression> with Expression::Literal(Box::new(Literal::Number(...)))
52910                self.parse_number()?.map(Box::new)
52911            } else {
52912                None
52913            };
52914
52915            // Unit: MINUTE, HOUR, DAY, etc.
52916            let unit = if every.is_some() {
52917                Some(self.expect_identifier_or_keyword()?.to_ascii_uppercase())
52918            } else {
52919                None
52920            };
52921
52922            // STARTS 'datetime'
52923            let starts = if self.match_identifier("STARTS") {
52924                let s = self.expect_string()?;
52925                Some(Box::new(Expression::Literal(Box::new(Literal::String(s)))))
52926            } else {
52927                None
52928            };
52929
52930            (every, unit, starts)
52931        } else {
52932            (None, None, None)
52933        };
52934
52935        Ok(RefreshTriggerProperty {
52936            method,
52937            kind,
52938            every,
52939            unit,
52940            starts,
52941        })
52942    }
52943
52944    /// parse_remote_with_connection - Implemented from Python _parse_remote_with_connection
52945    #[allow(unused_variables, unused_mut)]
52946    pub fn parse_remote_with_connection(&mut self) -> Result<Option<Expression>> {
52947        if self.match_text_seq(&["WITH", "CONNECTION"]) {
52948            // Matched: WITH CONNECTION
52949            return Ok(None);
52950        }
52951        Ok(None)
52952    }
52953
52954    /// parse_respect_or_ignore_nulls - Implemented from Python _parse_respect_or_ignore_nulls
52955    #[allow(unused_variables, unused_mut)]
52956    pub fn parse_respect_or_ignore_nulls(&mut self) -> Result<Option<Expression>> {
52957        if self.match_text_seq(&["IGNORE", "NULLS"]) {
52958            // Matched: IGNORE NULLS
52959            return Ok(None);
52960        }
52961        if self.match_text_seq(&["RESPECT", "NULLS"]) {
52962            // Matched: RESPECT NULLS
52963            return Ok(None);
52964        }
52965        Ok(None)
52966    }
52967
52968    /// parse_retention_period - Parses HISTORY_RETENTION_PERIOD (TSQL)
52969    /// Python: _parse_retention_period
52970    /// Format: INFINITE | <number> DAY | DAYS | MONTH | MONTHS | YEAR | YEARS
52971    pub fn parse_retention_period(&mut self) -> Result<Option<Expression>> {
52972        // Try to parse a number first
52973        let number = self.parse_number()?;
52974        let number_str = number
52975            .map(|n| match n {
52976                Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
52977                    let Literal::Number(s) = lit.as_ref() else {
52978                        unreachable!()
52979                    };
52980                    format!("{} ", s)
52981                }
52982                _ => String::new(),
52983            })
52984            .unwrap_or_default();
52985
52986        // Parse the unit (any token as a variable)
52987        let unit = self.parse_var_any_token()?;
52988        let unit_str = unit
52989            .map(|u| match u {
52990                Expression::Var(v) => v.this.clone(),
52991                _ => String::new(),
52992            })
52993            .unwrap_or_default();
52994
52995        let result = format!("{}{}", number_str, unit_str);
52996        Ok(Some(Expression::Var(Box::new(Var { this: result }))))
52997    }
52998
52999    /// parse_var_any_token - Parses any token as a Var (for flexible parsing)
53000    fn parse_var_any_token(&mut self) -> Result<Option<Expression>> {
53001        if !self.is_at_end() {
53002            let token = self.advance();
53003            Ok(Some(Expression::Var(Box::new(Var {
53004                this: token.text.clone(),
53005            }))))
53006        } else {
53007            Ok(None)
53008        }
53009    }
53010
53011    /// parse_returning - Creates Returning expression
53012    /// Parses RETURNING clause (PostgreSQL) for INSERT/UPDATE/DELETE
53013    #[allow(unused_variables, unused_mut)]
53014    pub fn parse_returning(&mut self) -> Result<Option<Expression>> {
53015        if !self.match_token(TokenType::Returning) {
53016            return Ok(None);
53017        }
53018
53019        // Parse expressions (column list or *)
53020        let expressions = self.parse_expression_list()?;
53021
53022        // Check for INTO target_table (Oracle style)
53023        let into = if self.match_token(TokenType::Into) {
53024            self.parse_table()?.map(Box::new)
53025        } else {
53026            None
53027        };
53028
53029        Ok(Some(Expression::Returning(Box::new(Returning {
53030            expressions,
53031            into,
53032        }))))
53033    }
53034
53035    /// parse_output_clause - Parses OUTPUT clause (TSQL)
53036    /// Used in INSERT/UPDATE/DELETE and MERGE statements
53037    /// Supports expressions with optional AS aliases: OUTPUT col1, col2 AS alias, col3
53038    pub fn parse_output_clause(&mut self) -> Result<OutputClause> {
53039        // Parse comma-separated list of columns/expressions with optional aliases
53040        let mut columns = Vec::new();
53041        loop {
53042            let expr = self.parse_expression()?;
53043            // Check for optional AS alias
53044            let expr = if self.match_token(TokenType::As) {
53045                let alias = self.expect_identifier_or_keyword_with_quoted()?;
53046                Expression::Alias(Box::new(Alias {
53047                    this: expr,
53048                    alias,
53049                    column_aliases: Vec::new(),
53050                    pre_alias_comments: Vec::new(),
53051                    trailing_comments: Vec::new(),
53052                    inferred_type: None,
53053                }))
53054            } else {
53055                expr
53056            };
53057            columns.push(expr);
53058            if !self.match_token(TokenType::Comma) {
53059                break;
53060            }
53061        }
53062
53063        // Check for INTO target
53064        let into_table = if self.match_token(TokenType::Into) {
53065            Some(self.parse_expression()?)
53066        } else {
53067            None
53068        };
53069
53070        Ok(OutputClause {
53071            columns,
53072            into_table,
53073        })
53074    }
53075
53076    /// parse_returns - Implemented from Python _parse_returns
53077    /// Calls: parse_types
53078    #[allow(unused_variables, unused_mut)]
53079    pub fn parse_returns(&mut self) -> Result<Option<Expression>> {
53080        if self.match_text_seq(&["NULL", "ON", "NULL", "INPUT"]) {
53081            return Ok(Some(Expression::Schema(Box::new(Schema {
53082                this: None,
53083                expressions: Vec::new(),
53084            }))));
53085        }
53086        Ok(None)
53087    }
53088
53089    /// parse_row - Parses ROW FORMAT clause
53090    /// Returns RowFormatSerdeProperty or RowFormatDelimitedProperty
53091    pub fn parse_row(&mut self) -> Result<Option<Expression>> {
53092        // Python: if not self._match(TokenType.FORMAT): return None
53093        if !self.match_token(TokenType::Format) {
53094            return Ok(None);
53095        }
53096        self.parse_row_format()
53097    }
53098
53099    /// parse_row_format - Implemented from Python _parse_row_format
53100    /// Parses SERDE or DELIMITED row format specifications
53101    pub fn parse_row_format(&mut self) -> Result<Option<Expression>> {
53102        // Check for SERDE row format
53103        if self.match_text_seq(&["SERDE"]) {
53104            let this = self.parse_string()?;
53105            let serde_properties = self.parse_serde_properties(false)?;
53106
53107            return Ok(Some(Expression::RowFormatSerdeProperty(Box::new(
53108                RowFormatSerdeProperty {
53109                    this: Box::new(this.unwrap_or(Expression::Null(Null))),
53110                    serde_properties: serde_properties.map(Box::new),
53111                },
53112            ))));
53113        }
53114
53115        // Check for DELIMITED row format
53116        self.match_text_seq(&["DELIMITED"]);
53117
53118        let mut fields = None;
53119        let mut escaped = None;
53120        let mut collection_items = None;
53121        let mut map_keys = None;
53122        let mut lines = None;
53123        let mut null = None;
53124
53125        // Parse FIELDS TERMINATED BY
53126        if self.match_text_seq(&["FIELDS", "TERMINATED", "BY"]) {
53127            fields = self.parse_string()?.map(Box::new);
53128            // Parse optional ESCAPED BY
53129            if self.match_text_seq(&["ESCAPED", "BY"]) {
53130                escaped = self.parse_string()?.map(Box::new);
53131            }
53132        }
53133
53134        // Parse COLLECTION ITEMS TERMINATED BY
53135        if self.match_text_seq(&["COLLECTION", "ITEMS", "TERMINATED", "BY"]) {
53136            collection_items = self.parse_string()?.map(Box::new);
53137        }
53138
53139        // Parse MAP KEYS TERMINATED BY
53140        if self.match_text_seq(&["MAP", "KEYS", "TERMINATED", "BY"]) {
53141            map_keys = self.parse_string()?.map(Box::new);
53142        }
53143
53144        // Parse LINES TERMINATED BY
53145        if self.match_text_seq(&["LINES", "TERMINATED", "BY"]) {
53146            lines = self.parse_string()?.map(Box::new);
53147        }
53148
53149        // Parse NULL DEFINED AS
53150        if self.match_text_seq(&["NULL", "DEFINED", "AS"]) {
53151            null = self.parse_string()?.map(Box::new);
53152        }
53153
53154        // Parse optional WITH SERDEPROPERTIES
53155        let serde = self.parse_serde_properties(false)?.map(Box::new);
53156
53157        Ok(Some(Expression::RowFormatDelimitedProperty(Box::new(
53158            RowFormatDelimitedProperty {
53159                fields,
53160                escaped,
53161                collection_items,
53162                map_keys,
53163                lines,
53164                null,
53165                serde,
53166            },
53167        ))))
53168    }
53169
53170    /// parse_schema - Ported from Python _parse_schema
53171    /// Parses schema definition: (col1 type1, col2 type2, ...)
53172    /// Used for CREATE TABLE column definitions
53173    #[allow(unused_variables, unused_mut)]
53174    pub fn parse_schema(&mut self) -> Result<Option<Expression>> {
53175        self.parse_schema_with_this(None)
53176    }
53177
53178    /// parse_schema_with_this - Parses schema with optional table reference
53179    fn parse_schema_with_this(&mut self, this: Option<Expression>) -> Result<Option<Expression>> {
53180        // Check for opening parenthesis
53181        if !self.match_token(TokenType::LParen) {
53182            return Ok(this.map(|e| e));
53183        }
53184
53185        // Check if this is a subquery (SELECT, WITH, etc.) not a schema
53186        if self.check(TokenType::Select) || self.check(TokenType::With) {
53187            // Retreat - put back the LParen
53188            self.current -= 1;
53189            return Ok(this.map(|e| e));
53190        }
53191
53192        // Parse column definitions and constraints
53193        let mut expressions = Vec::new();
53194        if !self.check(TokenType::RParen) {
53195            loop {
53196                // Try to parse constraint first, then field definition
53197                if let Some(constraint) = self.parse_constraint()? {
53198                    expressions.push(constraint);
53199                } else if let Some(field_def) = self.parse_field_def()? {
53200                    expressions.push(field_def);
53201                } else {
53202                    break;
53203                }
53204
53205                if !self.match_token(TokenType::Comma) {
53206                    break;
53207                }
53208            }
53209        }
53210
53211        self.expect(TokenType::RParen)?;
53212
53213        Ok(Some(Expression::Schema(Box::new(Schema {
53214            this: this.map(Box::new),
53215            expressions,
53216        }))))
53217    }
53218
53219    /// Parse schema identifier: name or name(columns)
53220    /// Used for TSQL ON filegroup (partition_column) syntax
53221    fn parse_schema_identifier(&mut self) -> Result<Expression> {
53222        // Parse the identifier (filegroup name)
53223        let name = self.expect_identifier_with_quoted()?;
53224        let name_expr = Expression::Identifier(name);
53225
53226        // Check for optional parenthesized columns
53227        if self.match_token(TokenType::LParen) {
53228            let mut columns = Vec::new();
53229            loop {
53230                let col = self.expect_identifier_with_quoted()?;
53231                columns.push(Expression::Identifier(col));
53232                if !self.match_token(TokenType::Comma) {
53233                    break;
53234                }
53235            }
53236            self.expect(TokenType::RParen)?;
53237            Ok(Expression::Schema(Box::new(Schema {
53238                this: Some(Box::new(name_expr)),
53239                expressions: columns,
53240            })))
53241        } else {
53242            // Just the identifier, no columns
53243            Ok(name_expr)
53244        }
53245    }
53246
53247    /// parse_security - Implemented from Python _parse_security
53248    #[allow(unused_variables, unused_mut)]
53249    pub fn parse_security(&mut self) -> Result<Option<Expression>> {
53250        if self.match_texts(&["NONE", "DEFINER", "INVOKER"]) {
53251            // Matched one of: NONE, DEFINER, INVOKER
53252            return Ok(None);
53253        }
53254        Ok(None)
53255    }
53256
53257    /// parse_select_or_expression - Parses either a SELECT statement or an expression
53258    /// Python: _parse_select_or_expression
53259    pub fn parse_select_or_expression(&mut self) -> Result<Option<Expression>> {
53260        // Save position for potential backtracking
53261        let start_pos = self.current;
53262
53263        // First try to parse a SELECT statement if we're at a SELECT keyword
53264        if self.check(TokenType::Select) {
53265            return Ok(Some(self.parse_select()?));
53266        }
53267
53268        // Otherwise try to parse an expression (assignment)
53269        if let Some(expr) = self.parse_disjunction()? {
53270            return Ok(Some(expr));
53271        }
53272
53273        // Backtrack if nothing worked
53274        self.current = start_pos;
53275
53276        Ok(None)
53277    }
53278
53279    /// parse_select_query - Implemented from Python _parse_select_query
53280    /// Calls: parse_string, parse_table, parse_describe
53281    #[allow(unused_variables, unused_mut)]
53282    pub fn parse_select_query(&mut self) -> Result<Option<Expression>> {
53283        if self.match_texts(&["STRUCT", "VALUE"]) {
53284            // Matched one of: STRUCT, VALUE
53285            return Ok(None);
53286        }
53287        Ok(None)
53288    }
53289
53290    /// parse_sequence_properties - Implemented from Python _parse_sequence_properties
53291    /// Calls: parse_number, parse_term, parse_column
53292    #[allow(unused_variables, unused_mut)]
53293    pub fn parse_sequence_properties(&mut self) -> Result<Option<Expression>> {
53294        if self.match_text_seq(&["INCREMENT"]) {
53295            return Ok(Some(Expression::SequenceProperties(Box::new(
53296                SequenceProperties {
53297                    increment: None,
53298                    minvalue: None,
53299                    maxvalue: None,
53300                    cache: None,
53301                    start: None,
53302                    owned: None,
53303                    options: Vec::new(),
53304                },
53305            ))));
53306        }
53307        if self.match_text_seq(&["BY"]) {
53308            // Matched: BY
53309            return Ok(None);
53310        }
53311        if self.match_text_seq(&["="]) {
53312            // Matched: =
53313            return Ok(None);
53314        }
53315        Ok(None)
53316    }
53317
53318    /// parse_serde_properties - Implemented from Python _parse_serde_properties
53319    /// Parses SERDEPROPERTIES clause: [WITH] SERDEPROPERTIES (key=value, ...)
53320    pub fn parse_serde_properties(&mut self, with_: bool) -> Result<Option<Expression>> {
53321        let start_index = self.current;
53322        let has_with = with_ || self.match_text_seq(&["WITH"]);
53323
53324        // Check for SERDEPROPERTIES keyword
53325        if !self.match_token(TokenType::SerdeProperties) {
53326            self.current = start_index;
53327            return Ok(None);
53328        }
53329
53330        // Parse wrapped properties manually since parse_property doesn't handle 'key'='value' syntax
53331        let mut expressions = Vec::new();
53332        if self.match_token(TokenType::LParen) {
53333            loop {
53334                if self.check(TokenType::RParen) {
53335                    break;
53336                }
53337                // Parse 'key'='value' or key=value
53338                let key = self.parse_primary()?;
53339                if self.match_token(TokenType::Eq) {
53340                    let value = self.parse_primary()?;
53341                    expressions.push(Expression::Eq(Box::new(BinaryOp::new(key, value))));
53342                } else {
53343                    expressions.push(key);
53344                }
53345                if !self.match_token(TokenType::Comma) {
53346                    break;
53347                }
53348            }
53349            self.expect(TokenType::RParen)?;
53350        }
53351
53352        Ok(Some(Expression::SerdeProperties(Box::new(
53353            SerdeProperties {
53354                expressions,
53355                with_: if has_with {
53356                    Some(Box::new(Expression::Boolean(BooleanLiteral {
53357                        value: true,
53358                    })))
53359                } else {
53360                    None
53361                },
53362            },
53363        ))))
53364    }
53365
53366    /// parse_session_parameter - Ported from Python _parse_session_parameter
53367    #[allow(unused_variables, unused_mut)]
53368    /// parse_session_parameter - Parses session parameters (@@var or @@session.var)
53369    /// Example: @@session.sql_mode, @@global.autocommit
53370    pub fn parse_session_parameter(&mut self) -> Result<Option<Expression>> {
53371        // Parse the first identifier or primary
53372        let first = if let Some(id) = self.parse_id_var()? {
53373            id
53374        } else if let Some(primary) = self.parse_primary_or_var()? {
53375            primary
53376        } else {
53377            return Ok(None);
53378        };
53379
53380        // Check for dot notation (kind.name)
53381        let (kind, this) = if self.match_token(TokenType::Dot) {
53382            // kind is the first part, parse the second
53383            let kind_name = match &first {
53384                Expression::Identifier(id) => Some(id.name.clone()),
53385                _ => None,
53386            };
53387            let second = self
53388                .parse_var()?
53389                .or_else(|| self.parse_primary_or_var().ok().flatten());
53390            (kind_name, second.unwrap_or(first))
53391        } else {
53392            (None, first)
53393        };
53394
53395        Ok(Some(Expression::SessionParameter(Box::new(
53396            SessionParameter {
53397                this: Box::new(this),
53398                kind,
53399            },
53400        ))))
53401    }
53402
53403    /// parse_set_item - Ported from Python _parse_set_item
53404    /// Parses an item in a SET statement (GLOBAL, LOCAL, SESSION prefixes, or assignment)
53405    #[allow(unused_variables, unused_mut)]
53406    pub fn parse_set_item(&mut self) -> Result<Option<Expression>> {
53407        // Check for specific prefixes
53408        let kind = if self.match_text_seq(&["GLOBAL"]) {
53409            Some("GLOBAL".to_string())
53410        } else if self.match_text_seq(&["LOCAL"]) {
53411            Some("LOCAL".to_string())
53412        } else if self.match_text_seq(&["SESSION"]) {
53413            Some("SESSION".to_string())
53414        } else {
53415            None
53416        };
53417
53418        // Delegate to set_item_assignment
53419        self.parse_set_item_assignment()
53420    }
53421
53422    /// parse_set_item_assignment - Implemented from Python _parse_set_item_assignment
53423    /// Parses SET variable = value assignments
53424    pub fn parse_set_item_assignment(&mut self) -> Result<Option<Expression>> {
53425        let start_index = self.current;
53426
53427        // Try to parse as TRANSACTION
53428        if self.match_text_seq(&["TRANSACTION"]) {
53429            // This is handled by parse_set_transaction
53430            return Ok(Some(Expression::SetItem(Box::new(SetItem {
53431                name: Expression::Var(Box::new(Var {
53432                    this: "TRANSACTION".to_string(),
53433                })),
53434                value: Expression::Null(Null),
53435                kind: None,
53436                no_equals: false,
53437            }))));
53438        }
53439
53440        // Parse left side: primary or column
53441        let left = self
53442            .parse_primary_or_var()?
53443            .or_else(|| self.parse_column().ok().flatten());
53444
53445        if left.is_none() {
53446            self.current = start_index;
53447            return Ok(None);
53448        }
53449
53450        // Check for assignment delimiter (= or TO or :=)
53451        if !self.match_texts(&["=", "TO", ":="]) {
53452            self.current = start_index;
53453            return Ok(None);
53454        }
53455
53456        // Parse right side: value
53457        // First try string literals (preserve quoting), then booleans/numbers, then identifiers
53458        let right_val = if self.check(TokenType::String) {
53459            let text = self.advance().text.clone();
53460            Expression::Literal(Box::new(Literal::String(text)))
53461        } else if self.check(TokenType::False) {
53462            self.skip();
53463            Expression::Boolean(BooleanLiteral { value: false })
53464        } else if self.check(TokenType::True) {
53465            self.skip();
53466            Expression::Boolean(BooleanLiteral { value: true })
53467        } else {
53468            let right = self
53469                .parse_id_var()?
53470                .or_else(|| self.parse_primary_or_var().ok().flatten());
53471            // Convert Column/Identifier to Var
53472            match right {
53473                Some(Expression::Column(col)) => Expression::Var(Box::new(Var {
53474                    this: col.name.name.clone(),
53475                })),
53476                Some(Expression::Identifier(id)) => Expression::Var(Box::new(Var {
53477                    this: id.name.clone(),
53478                })),
53479                Some(other) => other,
53480                None => Expression::Null(Null),
53481            }
53482        };
53483
53484        Ok(Some(Expression::SetItem(Box::new(SetItem {
53485            name: left
53486                .ok_or_else(|| self.parse_error("Expected variable name in SET statement"))?,
53487            value: right_val,
53488            kind: None,
53489            no_equals: false,
53490        }))))
53491    }
53492
53493    /// parse_set_operations - Parses UNION/INTERSECT/EXCEPT operations
53494    /// This version parses from current position (expects to be at set operator)
53495    /// Python: _parse_set_operations
53496    pub fn parse_set_operations(&mut self) -> Result<Option<Expression>> {
53497        // Parse a SELECT or subquery first
53498        let left = if self.check(TokenType::Select) {
53499            Some(self.parse_select()?)
53500        } else if self.match_token(TokenType::LParen) {
53501            let inner = self.parse_select()?;
53502            self.match_token(TokenType::RParen);
53503            Some(inner)
53504        } else {
53505            None
53506        };
53507
53508        if left.is_none() {
53509            return Ok(None);
53510        }
53511
53512        self.parse_set_operations_with_expr(left)
53513    }
53514
53515    /// parse_set_operations_with_expr - Parses set operations with a left expression
53516    pub fn parse_set_operations_with_expr(
53517        &mut self,
53518        this: Option<Expression>,
53519    ) -> Result<Option<Expression>> {
53520        let mut result = this;
53521
53522        while result.is_some() {
53523            if let Some(setop) = self.parse_set_operation_with_expr(result.clone())? {
53524                result = Some(setop);
53525            } else {
53526                break;
53527            }
53528        }
53529
53530        Ok(result)
53531    }
53532
53533    /// parse_set_operation_with_expr - Parses a single set operation (UNION, INTERSECT, EXCEPT)
53534    fn parse_set_operation_with_expr(
53535        &mut self,
53536        left: Option<Expression>,
53537    ) -> Result<Option<Expression>> {
53538        let left_expr = match left {
53539            Some(e) => e,
53540            None => return Ok(None),
53541        };
53542
53543        // Check for UNION, INTERSECT, EXCEPT
53544        let op_type = if self.match_token(TokenType::Union) {
53545            "UNION"
53546        } else if self.match_token(TokenType::Intersect) {
53547            "INTERSECT"
53548        } else if self.match_token(TokenType::Except) {
53549            "EXCEPT"
53550        } else {
53551            return Ok(Some(left_expr));
53552        };
53553
53554        // Check for ALL or DISTINCT
53555        let (all, distinct) = if self.match_token(TokenType::All) {
53556            (true, false)
53557        } else {
53558            let d = self.match_token(TokenType::Distinct);
53559            (false, d)
53560        };
53561
53562        // DuckDB: UNION [ALL] BY NAME SELECT ...
53563        let by_name = self.match_token(TokenType::By) && self.match_identifier("NAME");
53564
53565        // Parse the right side (SELECT or subquery)
53566        let right = if self.check(TokenType::Select) {
53567            self.parse_select()?
53568        } else if self.match_token(TokenType::LParen) {
53569            let inner = self.parse_select()?;
53570            self.match_token(TokenType::RParen);
53571            inner
53572        } else {
53573            return Ok(Some(left_expr));
53574        };
53575
53576        // Create the appropriate set operation expression
53577        match op_type {
53578            "UNION" => Ok(Some(Expression::Union(Box::new(Union {
53579                left: left_expr,
53580                right,
53581                all,
53582                distinct,
53583                with: None,
53584                order_by: None,
53585                limit: None,
53586                offset: None,
53587                distribute_by: None,
53588                sort_by: None,
53589                cluster_by: None,
53590                by_name,
53591                side: None,
53592                kind: None,
53593                corresponding: false,
53594                strict: false,
53595                on_columns: Vec::new(),
53596            })))),
53597            "INTERSECT" => Ok(Some(Expression::Intersect(Box::new(Intersect {
53598                left: left_expr,
53599                right,
53600                all,
53601                distinct,
53602                with: None,
53603                order_by: None,
53604                limit: None,
53605                offset: None,
53606                distribute_by: None,
53607                sort_by: None,
53608                cluster_by: None,
53609                by_name,
53610                side: None,
53611                kind: None,
53612                corresponding: false,
53613                strict: false,
53614                on_columns: Vec::new(),
53615            })))),
53616            "EXCEPT" => Ok(Some(Expression::Except(Box::new(Except {
53617                left: left_expr,
53618                right,
53619                all,
53620                distinct,
53621                with: None,
53622                order_by: None,
53623                limit: None,
53624                offset: None,
53625                distribute_by: None,
53626                sort_by: None,
53627                cluster_by: None,
53628                by_name,
53629                side: None,
53630                kind: None,
53631                corresponding: false,
53632                strict: false,
53633                on_columns: Vec::new(),
53634            })))),
53635            _ => Ok(Some(left_expr)),
53636        }
53637    }
53638
53639    /// parse_set_transaction - Implemented from Python _parse_set_transaction
53640    #[allow(unused_variables, unused_mut)]
53641    pub fn parse_set_transaction(&mut self) -> Result<Option<Expression>> {
53642        if self.match_text_seq(&["TRANSACTION"]) {
53643            // Matched: TRANSACTION
53644            return Ok(None);
53645        }
53646        Ok(None)
53647    }
53648
53649    /// Helper to consume an optional ClickHouse SETTINGS clause
53650    /// Used in SHOW, CHECK TABLE, and other ClickHouse statements
53651    fn parse_clickhouse_settings_clause(&mut self) -> Result<()> {
53652        if self.match_token(TokenType::Settings) {
53653            let _ = self.parse_settings_property()?;
53654        }
53655        Ok(())
53656    }
53657
53658    /// parse_settings_property - Parses SETTINGS property (ClickHouse)
53659    /// Python: _parse_settings_property
53660    /// Format: SETTINGS key=value, key=value, ...
53661    pub fn parse_settings_property(&mut self) -> Result<Option<Expression>> {
53662        // Parse comma-separated assignment expressions
53663        let mut expressions = Vec::new();
53664        loop {
53665            if let Some(assignment) = self.parse_assignment()? {
53666                expressions.push(assignment);
53667            } else {
53668                break;
53669            }
53670            if !self.match_token(TokenType::Comma) {
53671                break;
53672            }
53673        }
53674
53675        Ok(Some(Expression::SettingsProperty(Box::new(
53676            SettingsProperty { expressions },
53677        ))))
53678    }
53679
53680    /// parse_simplified_pivot - Ported from Python _parse_simplified_pivot
53681    /// Handles DuckDB simplified PIVOT/UNPIVOT syntax:
53682    ///   PIVOT table ON columns [IN (...)] USING agg_func [AS alias], ... [GROUP BY ...]
53683    ///   UNPIVOT table ON columns [INTO NAME col VALUE col, ...]
53684    #[allow(unused_variables, unused_mut)]
53685    pub fn parse_simplified_pivot(&mut self, is_unpivot: bool) -> Result<Option<Expression>> {
53686        // Parse the source table (can be a subquery like (SELECT 1 AS col1, 2 AS col2))
53687        let this = if self.check(TokenType::LParen) {
53688            // Could be parenthesized subquery
53689            self.skip(); // consume (
53690            if self.check(TokenType::Select) || self.check(TokenType::With) {
53691                let inner = self.parse_statement()?;
53692                self.expect(TokenType::RParen)?;
53693                Some(Expression::Subquery(Box::new(Subquery {
53694                    this: inner,
53695                    alias: None,
53696                    column_aliases: Vec::new(),
53697                    order_by: None,
53698                    limit: None,
53699                    offset: None,
53700                    lateral: false,
53701                    modifiers_inside: false,
53702                    trailing_comments: Vec::new(),
53703                    distribute_by: None,
53704                    sort_by: None,
53705                    cluster_by: None,
53706                    inferred_type: None,
53707                })))
53708            } else {
53709                // Not a subquery, retreat and parse as expression in parens
53710                self.current -= 1; // un-consume the (
53711                Some(self.parse_primary()?)
53712            }
53713        } else {
53714            // Parse table reference (e.g., Cities, schema.table, duckdb_functions())
53715            Some(self.parse_primary()?)
53716        };
53717
53718        // Parse ON columns
53719        let expressions = if self.match_text_seq(&["ON"]) {
53720            let mut on_exprs = Vec::new();
53721            loop {
53722                // Parse ON expression - use parse_bitwise to handle complex expressions like Country || '_' || Name
53723                let on_expr = self.parse_bitwise()?;
53724                if on_expr.is_none() {
53725                    break;
53726                }
53727                let mut expr = on_expr.unwrap();
53728
53729                // Check for IN clause on this column
53730                if self.match_token(TokenType::In) {
53731                    if self.match_token(TokenType::LParen) {
53732                        let mut in_exprs = Vec::new();
53733                        loop {
53734                            if self.check(TokenType::RParen) {
53735                                break;
53736                            }
53737                            if let Some(val) = self.parse_select_or_expression()? {
53738                                in_exprs.push(val);
53739                            }
53740                            if !self.match_token(TokenType::Comma) {
53741                                break;
53742                            }
53743                        }
53744                        self.expect(TokenType::RParen)?;
53745                        expr = Expression::In(Box::new(In {
53746                            this: expr,
53747                            expressions: in_exprs,
53748                            query: None,
53749                            not: false,
53750                            global: false,
53751                            unnest: None,
53752                            is_field: false,
53753                        }));
53754                    }
53755                }
53756                // Check for alias (UNPIVOT ON (jan, feb, mar) AS q1, ...)
53757                else if self.match_token(TokenType::As) {
53758                    let alias_name = self.expect_identifier()?;
53759                    expr =
53760                        Expression::Alias(Box::new(Alias::new(expr, Identifier::new(alias_name))));
53761                }
53762
53763                on_exprs.push(expr);
53764
53765                // Continue if comma
53766                if !self.match_token(TokenType::Comma) {
53767                    break;
53768                }
53769            }
53770            on_exprs
53771        } else {
53772            Vec::new()
53773        };
53774
53775        // Parse INTO for UNPIVOT columns (INTO NAME col VALUE col, ...)
53776        let into = self.parse_unpivot_columns()?;
53777
53778        // Parse USING clause (aggregation functions with optional aliases)
53779        // e.g., USING SUM(Population), USING SUM(Population) AS total, MAX(Population) AS max
53780        // e.g., USING CAST(AVG(LENGTH(function_name)) AS INT)
53781        let using = if self.match_text_seq(&["USING"]) {
53782            let mut using_exprs = Vec::new();
53783            loop {
53784                // Stop if we hit GROUP BY or end of input
53785                if self.is_at_end() || self.check(TokenType::Group) || self.check(TokenType::RParen)
53786                {
53787                    break;
53788                }
53789                // Parse the primary expression (function call, possibly with cast :: operator)
53790                let func = self.parse_primary()?;
53791                // Check for :: cast operator (e.g., SUM(Population)::INTEGER)
53792                let expr = if self.match_token(TokenType::DColon) {
53793                    let data_type = self.parse_data_type()?;
53794                    Expression::Cast(Box::new(Cast {
53795                        this: func,
53796                        to: data_type,
53797                        trailing_comments: Vec::new(),
53798                        double_colon_syntax: true,
53799                        format: None,
53800                        default: None,
53801                        inferred_type: None,
53802                    }))
53803                } else {
53804                    func
53805                };
53806                // Try to parse alias (AS alias)
53807                if self.match_token(TokenType::As) {
53808                    let alias_name = self.expect_identifier()?;
53809                    using_exprs.push(Expression::Alias(Box::new(Alias::new(
53810                        expr,
53811                        Identifier::new(alias_name),
53812                    ))));
53813                } else {
53814                    using_exprs.push(expr);
53815                }
53816                if !self.match_token(TokenType::Comma) {
53817                    break;
53818                }
53819            }
53820            using_exprs
53821        } else {
53822            Vec::new()
53823        };
53824
53825        // Parse optional GROUP BY
53826        let group = self.parse_group()?;
53827
53828        let source = this.unwrap();
53829
53830        Ok(Some(Expression::Pivot(Box::new(Pivot {
53831            this: source,
53832            expressions,
53833            fields: Vec::new(),
53834            using,
53835            group: group.map(Box::new),
53836            unpivot: is_unpivot,
53837            into: into.map(Box::new),
53838            alias: None,
53839            include_nulls: None,
53840            default_on_null: None,
53841            with: None,
53842        }))))
53843    }
53844
53845    /// parse_slice - Parses array slice syntax [start:end:step]
53846    /// Python: _parse_slice
53847    /// Takes an optional 'this' expression (the start of the slice)
53848    pub fn parse_slice(&mut self) -> Result<Option<Expression>> {
53849        self.parse_slice_with_this(None)
53850    }
53851
53852    /// Implementation of parse_slice with 'this' parameter
53853    pub fn parse_slice_with_this(
53854        &mut self,
53855        this: Option<Expression>,
53856    ) -> Result<Option<Expression>> {
53857        // Check for colon - if not found, return this as-is
53858        if !self.match_token(TokenType::Colon) {
53859            return Ok(this);
53860        }
53861
53862        // Parse end expression
53863        // Handle special case: -: which means -1 (from end)
53864        let end = if self.check(TokenType::Dash) && self.check_next(TokenType::Colon) {
53865            // -: pattern means -1 (from end)
53866            self.skip(); // consume dash
53867            Some(Expression::Neg(Box::new(UnaryOp::new(
53868                Expression::Literal(Box::new(Literal::Number("1".to_string()))),
53869            ))))
53870        } else if self.check(TokenType::Colon) || self.check(TokenType::RBracket) {
53871            // Empty end like [start::step] or [start:]
53872            None
53873        } else {
53874            Some(self.parse_unary()?)
53875        };
53876
53877        // Parse optional step expression after second colon
53878        let step = if self.match_token(TokenType::Colon) {
53879            if self.check(TokenType::RBracket) {
53880                None
53881            } else {
53882                Some(self.parse_unary()?)
53883            }
53884        } else {
53885            None
53886        };
53887
53888        Ok(Some(Expression::Slice(Box::new(Slice {
53889            this: this.map(Box::new),
53890            expression: end.map(Box::new),
53891            step: step.map(Box::new),
53892        }))))
53893    }
53894
53895    /// Parse a slice element (start, end, or step in array slicing)
53896    /// This uses parse_unary to avoid interpreting : as parameter syntax
53897    /// Returns None for empty elements (e.g., [:] or [::step])
53898    fn parse_slice_element(&mut self) -> Result<Option<Expression>> {
53899        // Check for empty element (next is : or ])
53900        if self.check(TokenType::Colon) || self.check(TokenType::RBracket) {
53901            return Ok(None);
53902        }
53903        // Handle special case: -: means -1 (from the end)
53904        // This is used in slicing like [:-:-1] where the first -: means end=-1
53905        if self.check(TokenType::Dash) && self.check_next(TokenType::Colon) {
53906            self.skip(); // consume dash
53907                         // Don't consume the colon - let the caller handle it
53908            return Ok(Some(Expression::Neg(Box::new(UnaryOp::new(
53909                Expression::Literal(Box::new(Literal::Number("1".to_string()))),
53910            )))));
53911        }
53912        // Parse full expression (including binary ops like y - 1) but stop at : or ]
53913        let expr = self.parse_disjunction()?;
53914        Ok(expr)
53915    }
53916
53917    /// parse_sort - Ported from Python _parse_sort
53918    /// Parses SORT BY clause (Hive/Spark)
53919    #[allow(unused_variables, unused_mut)]
53920    pub fn parse_sort(&mut self) -> Result<Option<Expression>> {
53921        // Check for SORT BY token
53922        if !self.match_keywords(&[TokenType::Sort, TokenType::By]) {
53923            return Ok(None);
53924        }
53925
53926        // Parse comma-separated ordered expressions
53927        let mut expressions = Vec::new();
53928        loop {
53929            if let Some(ordered) = self.parse_ordered_item()? {
53930                expressions.push(ordered);
53931            } else {
53932                break;
53933            }
53934            if !self.match_token(TokenType::Comma) {
53935                break;
53936            }
53937        }
53938
53939        Ok(Some(Expression::SortBy(Box::new(SortBy { expressions }))))
53940    }
53941
53942    /// parse_cluster_by_clause - Parses CLUSTER BY clause (Hive/Spark)
53943    #[allow(unused_variables, unused_mut)]
53944    pub fn parse_cluster_by_clause(&mut self) -> Result<Option<Expression>> {
53945        if !self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
53946            return Ok(None);
53947        }
53948
53949        // Parse comma-separated ordered expressions
53950        let mut expressions: Vec<Ordered> = Vec::new();
53951        loop {
53952            if let Some(ordered) = self.parse_ordered_item()? {
53953                expressions.push(ordered);
53954            } else {
53955                break;
53956            }
53957            if !self.match_token(TokenType::Comma) {
53958                break;
53959            }
53960        }
53961        Ok(Some(Expression::ClusterBy(Box::new(ClusterBy {
53962            expressions,
53963        }))))
53964    }
53965
53966    /// parse_distribute_by_clause - Parses DISTRIBUTE BY clause (Hive/Spark)
53967    #[allow(unused_variables, unused_mut)]
53968    pub fn parse_distribute_by_clause(&mut self) -> Result<Option<Expression>> {
53969        if !self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
53970            return Ok(None);
53971        }
53972
53973        let expressions = self.parse_expression_list()?;
53974        Ok(Some(Expression::DistributeBy(Box::new(DistributeBy {
53975            expressions,
53976        }))))
53977    }
53978
53979    /// parse_sortkey - Redshift/PostgreSQL SORTKEY property
53980    /// Parses SORTKEY(column1, column2, ...) with optional COMPOUND modifier
53981    #[allow(unused_variables, unused_mut)]
53982    pub fn parse_sortkey(&mut self) -> Result<Option<Expression>> {
53983        // Parse the wrapped list of columns/identifiers
53984        let this = if self.match_token(TokenType::LParen) {
53985            let mut columns = Vec::new();
53986            loop {
53987                if let Some(id) = self.parse_id_var()? {
53988                    columns.push(id);
53989                } else {
53990                    break;
53991                }
53992                if !self.match_token(TokenType::Comma) {
53993                    break;
53994                }
53995            }
53996            self.match_token(TokenType::RParen);
53997
53998            if columns.is_empty() {
53999                return Ok(None);
54000            }
54001
54002            if columns.len() == 1 {
54003                columns.into_iter().next().unwrap()
54004            } else {
54005                Expression::Tuple(Box::new(Tuple {
54006                    expressions: columns,
54007                }))
54008            }
54009        } else {
54010            // Single column without parens
54011            if let Some(id) = self.parse_id_var()? {
54012                id
54013            } else {
54014                return Ok(None);
54015            }
54016        };
54017
54018        Ok(Some(Expression::SortKeyProperty(Box::new(
54019            SortKeyProperty {
54020                this: Box::new(this),
54021                compound: None, // compound is set by caller if COMPOUND keyword was matched
54022            },
54023        ))))
54024    }
54025
54026    /// parse_star - Parse STAR (*) token with optional EXCEPT/REPLACE/RENAME
54027    /// Python: if self._match(TokenType.STAR): return self._parse_star_ops()
54028    pub fn parse_star(&mut self) -> Result<Option<Expression>> {
54029        if !self.match_token(TokenType::Star) {
54030            return Ok(None);
54031        }
54032
54033        // Parse optional EXCEPT/EXCLUDE columns
54034        let except = self.parse_star_except()?;
54035
54036        // Parse optional REPLACE expressions
54037        let replace = self.parse_star_replace()?;
54038
54039        // Parse optional RENAME columns
54040        let rename = self.parse_star_rename()?;
54041
54042        Ok(Some(Expression::Star(Star {
54043            table: None,
54044            except,
54045            replace,
54046            rename,
54047            trailing_comments: Vec::new(),
54048            span: None,
54049        })))
54050    }
54051
54052    /// try_parse_identifier - Try to parse an identifier, returning None if not found
54053    fn try_parse_identifier(&mut self) -> Option<Identifier> {
54054        if self.is_identifier_token() {
54055            let token = self.advance();
54056            let quoted = token.token_type == TokenType::QuotedIdentifier;
54057            Some(Identifier {
54058                name: token.text,
54059                quoted,
54060                trailing_comments: Vec::new(),
54061                span: None,
54062            })
54063        } else {
54064            None
54065        }
54066    }
54067
54068    /// parse_star_except - Parse EXCEPT/EXCLUDE clause for Star
54069    /// Example: * EXCEPT (col1, col2)
54070    fn parse_star_except(&mut self) -> Result<Option<Vec<Identifier>>> {
54071        if !self.match_texts(&["EXCEPT", "EXCLUDE"]) {
54072            return Ok(None);
54073        }
54074
54075        // Parse (col1, col2, ...)
54076        if self.match_token(TokenType::LParen) {
54077            let mut columns = Vec::new();
54078            loop {
54079                if let Some(id) = self.try_parse_identifier() {
54080                    columns.push(id);
54081                } else if self.is_safe_keyword_as_identifier() {
54082                    // ClickHouse: allow keywords like 'key' as column names in EXCEPT
54083                    let token = self.advance();
54084                    columns.push(Identifier {
54085                        name: token.text,
54086                        quoted: false,
54087                        trailing_comments: Vec::new(),
54088                        span: None,
54089                    });
54090                } else {
54091                    break;
54092                }
54093                if !self.match_token(TokenType::Comma) {
54094                    break;
54095                }
54096            }
54097            self.match_token(TokenType::RParen);
54098            return Ok(Some(columns));
54099        }
54100
54101        // Single column without parens
54102        if let Some(id) = self.try_parse_identifier() {
54103            return Ok(Some(vec![id]));
54104        }
54105
54106        Ok(None)
54107    }
54108
54109    /// parse_star_replace - Parse REPLACE clause for Star
54110    /// Example: * REPLACE (col1 AS alias1, col2 AS alias2)
54111    fn parse_star_replace(&mut self) -> Result<Option<Vec<Alias>>> {
54112        if !self.match_texts(&["REPLACE"]) {
54113            return Ok(None);
54114        }
54115
54116        if self.match_token(TokenType::LParen) {
54117            let mut aliases = Vec::new();
54118            loop {
54119                // Parse expression AS alias
54120                if let Some(expr) = self.parse_disjunction()? {
54121                    let alias_name = if self.match_token(TokenType::As) {
54122                        self.try_parse_identifier()
54123                    } else {
54124                        None
54125                    };
54126
54127                    aliases.push(Alias {
54128                        this: expr,
54129                        alias: alias_name.unwrap_or_else(|| Identifier::new("")),
54130                        column_aliases: Vec::new(),
54131                        pre_alias_comments: Vec::new(),
54132                        trailing_comments: Vec::new(),
54133                        inferred_type: None,
54134                    });
54135                } else {
54136                    break;
54137                }
54138                if !self.match_token(TokenType::Comma) {
54139                    break;
54140                }
54141            }
54142            self.match_token(TokenType::RParen);
54143            return Ok(Some(aliases));
54144        }
54145
54146        Ok(None)
54147    }
54148
54149    /// parse_star_rename - Parse RENAME clause for Star
54150    /// Example: * RENAME (old_col AS new_col, ...)
54151    fn parse_star_rename(&mut self) -> Result<Option<Vec<(Identifier, Identifier)>>> {
54152        if !self.match_texts(&["RENAME"]) {
54153            return Ok(None);
54154        }
54155
54156        if self.match_token(TokenType::LParen) {
54157            let mut renames = Vec::new();
54158            loop {
54159                // Parse old_name AS new_name
54160                if let Some(old_name) = self.try_parse_identifier() {
54161                    if self.match_token(TokenType::As) {
54162                        if let Some(new_name) = self.try_parse_identifier() {
54163                            renames.push((old_name, new_name));
54164                        }
54165                    }
54166                } else {
54167                    break;
54168                }
54169                if !self.match_token(TokenType::Comma) {
54170                    break;
54171                }
54172            }
54173            self.match_token(TokenType::RParen);
54174            return Ok(Some(renames));
54175        }
54176
54177        Ok(None)
54178    }
54179
54180    /// parse_star_op - Helper to parse EXCEPT/REPLACE/RENAME with keywords
54181    /// Returns list of expressions if keywords match
54182    pub fn parse_star_op(&mut self, keywords: &[&str]) -> Result<Option<Vec<Expression>>> {
54183        if !self.match_texts(keywords) {
54184            return Ok(None);
54185        }
54186
54187        // If followed by paren, parse wrapped CSV
54188        if self.match_token(TokenType::LParen) {
54189            let expressions = self.parse_expression_list()?;
54190            self.match_token(TokenType::RParen);
54191            return Ok(Some(expressions));
54192        }
54193
54194        // Otherwise parse single aliased expression
54195        if let Some(expr) = self.parse_disjunction()? {
54196            // Try to parse explicit alias
54197            let result = if self.match_token(TokenType::As) {
54198                if let Some(alias_name) = self.try_parse_identifier() {
54199                    Expression::Alias(Box::new(Alias {
54200                        this: expr,
54201                        alias: alias_name,
54202                        column_aliases: Vec::new(),
54203                        pre_alias_comments: Vec::new(),
54204                        trailing_comments: Vec::new(),
54205                        inferred_type: None,
54206                    }))
54207                } else {
54208                    expr
54209                }
54210            } else {
54211                expr
54212            };
54213            return Ok(Some(vec![result]));
54214        }
54215
54216        Ok(None)
54217    }
54218
54219    /// parse_star_ops - Implemented from Python _parse_star_ops
54220    /// Creates a Star expression with EXCEPT/REPLACE/RENAME clauses
54221    /// Also handles * COLUMNS(pattern) syntax for DuckDB column selection
54222    pub fn parse_star_ops(&mut self) -> Result<Option<Expression>> {
54223        // Handle * COLUMNS(pattern) function (DuckDB)
54224        // This parses patterns like: * COLUMNS(c ILIKE '%suffix')
54225        if self.match_text_seq(&["COLUMNS"]) && self.check(TokenType::LParen) {
54226            // Parse the COLUMNS function arguments
54227            self.expect(TokenType::LParen)?;
54228            let this = self.parse_expression()?;
54229            self.expect(TokenType::RParen)?;
54230
54231            // Return a Columns expression with unpack=true (since it came from * COLUMNS())
54232            return Ok(Some(Expression::Columns(Box::new(Columns {
54233                this: Box::new(this),
54234                unpack: Some(Box::new(Expression::Boolean(BooleanLiteral {
54235                    value: true,
54236                }))),
54237            }))));
54238        }
54239
54240        // Parse EXCEPT/EXCLUDE
54241        let except_exprs = self.parse_star_op(&["EXCEPT", "EXCLUDE"])?;
54242        let except = except_exprs.map(|exprs| {
54243            exprs
54244                .into_iter()
54245                .filter_map(|e| match e {
54246                    Expression::Identifier(id) => Some(id),
54247                    Expression::Column(col) => Some(col.name),
54248                    _ => None,
54249                })
54250                .collect()
54251        });
54252
54253        // Parse REPLACE
54254        let replace_exprs = self.parse_star_op(&["REPLACE"])?;
54255        let replace = replace_exprs.map(|exprs| {
54256            exprs
54257                .into_iter()
54258                .filter_map(|e| match e {
54259                    Expression::Alias(a) => Some(*a),
54260                    _ => None,
54261                })
54262                .collect()
54263        });
54264
54265        // Parse RENAME
54266        let _rename_exprs = self.parse_star_op(&["RENAME"])?;
54267        let rename: Option<Vec<(Identifier, Identifier)>> = None; // Complex to extract from expressions
54268
54269        Ok(Some(Expression::Star(Star {
54270            table: None,
54271            except,
54272            replace,
54273            rename,
54274            trailing_comments: Vec::new(),
54275            span: None,
54276        })))
54277    }
54278
54279    /// parse_stored - Implemented from Python _parse_stored
54280    #[allow(unused_variables, unused_mut)]
54281    pub fn parse_stored(&mut self) -> Result<Option<Expression>> {
54282        if self.match_text_seq(&["BY"]) {
54283            return Ok(Some(Expression::InputOutputFormat(Box::new(
54284                InputOutputFormat {
54285                    input_format: None,
54286                    output_format: None,
54287                },
54288            ))));
54289        }
54290        if self.match_text_seq(&["INPUTFORMAT"]) {
54291            // Matched: INPUTFORMAT
54292            return Ok(None);
54293        }
54294        Ok(None)
54295    }
54296
54297    /// parse_stream - Implemented from Python _parse_stream
54298    #[allow(unused_variables, unused_mut)]
54299    pub fn parse_stream(&mut self) -> Result<Option<Expression>> {
54300        if self.match_text_seq(&["STREAM"]) {
54301            // Matched: STREAM
54302            return Ok(None);
54303        }
54304        Ok(None)
54305    }
54306
54307    /// parse_string - Parse string literal
54308    /// Python: if self._match_set(self.STRING_PARSERS): return STRING_PARSERS[token_type](...)
54309    pub fn parse_string(&mut self) -> Result<Option<Expression>> {
54310        // Regular string literal
54311        if self.match_token(TokenType::String) {
54312            let text = self.previous().text.clone();
54313            return Ok(Some(Expression::Literal(Box::new(Literal::String(text)))));
54314        }
54315        // National string (N'...')
54316        if self.match_token(TokenType::NationalString) {
54317            let text = self.previous().text.clone();
54318            return Ok(Some(Expression::Literal(Box::new(
54319                Literal::NationalString(text),
54320            ))));
54321        }
54322        // Raw string (r"..." or r'...')
54323        if self.match_token(TokenType::RawString) {
54324            let text = self.previous().text.clone();
54325            return Ok(Some(Expression::Literal(Box::new(Literal::RawString(
54326                text,
54327            )))));
54328        }
54329        // Heredoc string
54330        if self.match_token(TokenType::HeredocString) {
54331            let text = self.previous().text.clone();
54332            return Ok(Some(Expression::Literal(Box::new(Literal::String(text)))));
54333        }
54334        // Hex string (X'...' or 0x...)
54335        if self.match_token(TokenType::HexString) {
54336            let text = self.previous().text.clone();
54337            return Ok(Some(Expression::Literal(Box::new(Literal::HexString(
54338                text,
54339            )))));
54340        }
54341        // Bit string (B'...')
54342        if self.match_token(TokenType::BitString) {
54343            let text = self.previous().text.clone();
54344            return Ok(Some(Expression::Literal(Box::new(Literal::BitString(
54345                text,
54346            )))));
54347        }
54348        // Byte string (b"..." - BigQuery style)
54349        if self.match_token(TokenType::ByteString) {
54350            let text = self.previous().text.clone();
54351            return Ok(Some(Expression::Literal(Box::new(Literal::ByteString(
54352                text,
54353            )))));
54354        }
54355        Ok(None)
54356    }
54357
54358    /// parse_string_agg - Parses STRING_AGG function arguments
54359    /// Python: parser.py:6849-6899
54360    /// Handles DISTINCT, separator, ORDER BY, ON OVERFLOW, WITHIN GROUP
54361    #[allow(unused_variables, unused_mut)]
54362    pub fn parse_string_agg(&mut self) -> Result<Option<Expression>> {
54363        // Check for DISTINCT
54364        let distinct = self.match_token(TokenType::Distinct);
54365
54366        // Parse main expression
54367        let this = self.parse_disjunction()?;
54368        if this.is_none() {
54369            return Ok(None);
54370        }
54371
54372        // Parse optional separator
54373        let separator = if self.match_token(TokenType::Comma) {
54374            self.parse_disjunction()?
54375        } else {
54376            None
54377        };
54378
54379        // Parse ON OVERFLOW clause
54380        let on_overflow = if self.match_text_seq(&["ON", "OVERFLOW"]) {
54381            if self.match_text_seq(&["ERROR"]) {
54382                Some(Box::new(Expression::Var(Box::new(Var {
54383                    this: "ERROR".to_string(),
54384                }))))
54385            } else {
54386                self.match_text_seq(&["TRUNCATE"]);
54387                let truncate_str = self.parse_string()?;
54388                let with_count = if self.match_text_seq(&["WITH", "COUNT"]) {
54389                    Some(true)
54390                } else if self.match_text_seq(&["WITHOUT", "COUNT"]) {
54391                    Some(false)
54392                } else {
54393                    None
54394                };
54395                Some(Box::new(Expression::OverflowTruncateBehavior(Box::new(
54396                    OverflowTruncateBehavior {
54397                        this: truncate_str.map(Box::new),
54398                        with_count: with_count
54399                            .map(|c| Box::new(Expression::Boolean(BooleanLiteral { value: c }))),
54400                    },
54401                ))))
54402            }
54403        } else {
54404            None
54405        };
54406
54407        // Parse ORDER BY or WITHIN GROUP
54408        let order_by = if self.match_token(TokenType::OrderBy) {
54409            Some(self.parse_expression_list()?)
54410        } else if self.match_text_seq(&["WITHIN", "GROUP"]) {
54411            self.match_token(TokenType::LParen);
54412            let order = self.parse_order()?;
54413            self.match_token(TokenType::RParen);
54414            order.map(|o| vec![o])
54415        } else {
54416            None
54417        };
54418
54419        // Return as GroupConcat (which is the canonical form for STRING_AGG)
54420        Ok(Some(Expression::GroupConcat(Box::new(GroupConcatFunc {
54421            this: this.unwrap(),
54422            separator: separator,
54423            order_by: None,
54424            distinct,
54425            filter: None,
54426            inferred_type: None,
54427        }))))
54428    }
54429
54430    /// parse_string_as_identifier - Parses a string literal as a quoted identifier
54431    /// Python: _parse_string_as_identifier
54432    /// Used for cases where a string can be used as an identifier (e.g., MySQL)
54433    pub fn parse_string_as_identifier(&mut self) -> Result<Option<Expression>> {
54434        if self.match_token(TokenType::String) {
54435            let text = self.previous().text.clone();
54436            // Remove quotes if present
54437            let name = if text.starts_with('\'') && text.ends_with('\'') && text.len() >= 2 {
54438                text[1..text.len() - 1].to_string()
54439            } else if text.starts_with('"') && text.ends_with('"') && text.len() >= 2 {
54440                text[1..text.len() - 1].to_string()
54441            } else {
54442                text
54443            };
54444
54445            Ok(Some(Expression::Identifier(Identifier {
54446                name,
54447                quoted: true,
54448                trailing_comments: Vec::new(),
54449                span: None,
54450            })))
54451        } else {
54452            Ok(None)
54453        }
54454    }
54455
54456    /// parse_struct_types - Delegates to parse_types
54457    #[allow(unused_variables, unused_mut)]
54458    pub fn parse_struct_types(&mut self) -> Result<Option<Expression>> {
54459        self.parse_types()
54460    }
54461
54462    /// parse_subquery - Ported from Python _parse_subquery
54463    /// Parses a parenthesized SELECT as subquery: (SELECT ...)
54464    #[allow(unused_variables, unused_mut)]
54465    pub fn parse_subquery(&mut self) -> Result<Option<Expression>> {
54466        // Check for opening paren
54467        if !self.match_token(TokenType::LParen) {
54468            return Ok(None);
54469        }
54470
54471        // Check if it's a SELECT or WITH statement
54472        if !self.check(TokenType::Select) && !self.check(TokenType::With) {
54473            // Not a subquery, retreat
54474            self.current -= 1;
54475            return Ok(None);
54476        }
54477
54478        // Parse the query
54479        let query = self.parse_statement()?;
54480        self.expect(TokenType::RParen)?;
54481
54482        // Parse optional table alias
54483        let alias = self.parse_table_alias_if_present()?;
54484
54485        Ok(Some(Expression::Subquery(Box::new(Subquery {
54486            this: query,
54487            alias,
54488            column_aliases: Vec::new(),
54489            order_by: None,
54490            limit: None,
54491            offset: None,
54492            lateral: false,
54493            modifiers_inside: false,
54494            trailing_comments: Vec::new(),
54495            distribute_by: None,
54496            sort_by: None,
54497            cluster_by: None,
54498            inferred_type: None,
54499        }))))
54500    }
54501
54502    /// Helper to parse table alias if present
54503    fn parse_table_alias_if_present(&mut self) -> Result<Option<Identifier>> {
54504        // Check for AS keyword
54505        let explicit_as = self.match_token(TokenType::As);
54506
54507        // ClickHouse: keywords can be used as table aliases when AS is explicit
54508        let is_keyword_alias = explicit_as
54509            && matches!(
54510                self.config.dialect,
54511                Some(crate::dialects::DialectType::ClickHouse)
54512            )
54513            && self.peek().token_type.is_keyword();
54514
54515        // Try to parse identifier
54516        if self.check(TokenType::Identifier)
54517            || self.check(TokenType::QuotedIdentifier)
54518            || is_keyword_alias
54519        {
54520            if is_keyword_alias
54521                && !self.check(TokenType::Identifier)
54522                && !self.check(TokenType::QuotedIdentifier)
54523            {
54524                let token = self.advance();
54525                return Ok(Some(Identifier::new(token.text)));
54526            }
54527            if let Some(Expression::Identifier(id)) = self.parse_identifier()? {
54528                return Ok(Some(id));
54529            }
54530        } else if explicit_as {
54531            // AS was present but no identifier follows - this is an error
54532            return Err(self.parse_error("Expected identifier after AS"));
54533        }
54534
54535        Ok(None)
54536    }
54537
54538    /// parse_substring - Ported from Python _parse_substring
54539    /// Parses SUBSTRING function with two syntax variants:
54540    /// 1. Standard SQL: SUBSTRING(str FROM start [FOR length])
54541    /// 2. Function style: SUBSTRING(str, start, length)
54542    #[allow(unused_variables, unused_mut)]
54543    pub fn parse_substring(&mut self) -> Result<Option<Expression>> {
54544        // Parse initial comma-separated arguments
54545        let mut args: Vec<Expression> = Vec::new();
54546
54547        // Parse first argument (the string)
54548        match self.parse_bitwise() {
54549            Ok(Some(expr)) => {
54550                let expr = self.try_clickhouse_func_arg_alias(expr);
54551                args.push(expr);
54552            }
54553            Ok(None) => return Ok(None),
54554            Err(e) => return Err(e),
54555        }
54556
54557        // Check for comma-separated additional arguments
54558        while self.match_token(TokenType::Comma) {
54559            match self.parse_bitwise() {
54560                Ok(Some(expr)) => {
54561                    let expr = self.try_clickhouse_func_arg_alias(expr);
54562                    args.push(expr);
54563                }
54564                Ok(None) => break,
54565                Err(e) => return Err(e),
54566            }
54567        }
54568
54569        // Check for FROM/FOR syntax (SQL standard)
54570        let mut start: Option<Expression> = None;
54571        let mut length: Option<Expression> = None;
54572        let mut from_for_syntax = false;
54573
54574        loop {
54575            if self.match_token(TokenType::From) {
54576                from_for_syntax = true;
54577                match self.parse_bitwise() {
54578                    Ok(Some(expr)) => {
54579                        let expr = self.try_clickhouse_func_arg_alias(expr);
54580                        start = Some(expr);
54581                    }
54582                    Ok(None) => {}
54583                    Err(e) => return Err(e),
54584                }
54585            } else if self.match_token(TokenType::For) {
54586                from_for_syntax = true;
54587                // If no start specified yet, default to 1
54588                if start.is_none() {
54589                    start = Some(Expression::Literal(Box::new(Literal::Number(
54590                        "1".to_string(),
54591                    ))));
54592                }
54593                match self.parse_bitwise() {
54594                    Ok(Some(expr)) => {
54595                        let expr = self.try_clickhouse_func_arg_alias(expr);
54596                        length = Some(expr);
54597                    }
54598                    Ok(None) => {}
54599                    Err(e) => return Err(e),
54600                }
54601            } else {
54602                break;
54603            }
54604        }
54605
54606        // Build the substring expression
54607        if args.is_empty() {
54608            return Ok(None);
54609        }
54610
54611        let this = args.remove(0);
54612
54613        // Determine start and length
54614        let final_start = if let Some(s) = start {
54615            s
54616        } else if !args.is_empty() {
54617            args.remove(0)
54618        } else {
54619            Expression::Literal(Box::new(Literal::Number("1".to_string())))
54620        };
54621
54622        let final_length = if length.is_some() {
54623            length
54624        } else if !args.is_empty() {
54625            Some(args.remove(0))
54626        } else {
54627            None
54628        };
54629
54630        Ok(Some(Expression::Substring(Box::new(SubstringFunc {
54631            this,
54632            start: final_start,
54633            length: final_length,
54634            from_for_syntax,
54635        }))))
54636    }
54637
54638    /// parse_system_versioning_property - Implemented from Python _parse_system_versioning_property
54639    /// Calls: parse_table_parts, parse_retention_period
54640    #[allow(unused_variables, unused_mut)]
54641    pub fn parse_system_versioning_property(&mut self) -> Result<Option<Expression>> {
54642        if self.match_text_seq(&["OFF"]) {
54643            return Ok(Some(Expression::WithSystemVersioningProperty(Box::new(
54644                WithSystemVersioningProperty {
54645                    on: None,
54646                    this: None,
54647                    data_consistency: None,
54648                    retention_period: None,
54649                    with_: None,
54650                },
54651            ))));
54652        }
54653        if self.match_text_seq(&["HISTORY_TABLE", "="]) {
54654            // Matched: HISTORY_TABLE =
54655            return Ok(None);
54656        }
54657        if self.match_text_seq(&["DATA_CONSISTENCY_CHECK", "="]) {
54658            // Matched: DATA_CONSISTENCY_CHECK =
54659            return Ok(None);
54660        }
54661        Ok(None)
54662    }
54663
54664    /// Parse PostgreSQL ROWS FROM syntax:
54665    /// ROWS FROM (func1(args) AS alias1(col1 type1, col2 type2), func2(...) AS alias2(...)) [WITH ORDINALITY] [AS outer_alias(...)]
54666    fn parse_rows_from(&mut self) -> Result<Expression> {
54667        // Expect opening paren
54668        self.expect(TokenType::LParen)?;
54669
54670        let mut expressions = Vec::new();
54671
54672        loop {
54673            // Parse each function expression inside ROWS FROM
54674            // Each element is: func_name(args) [AS alias(col1 type1, col2 type2, ...)]
54675            let func_expr = self.parse_rows_from_function()?;
54676            expressions.push(func_expr);
54677
54678            if !self.match_token(TokenType::Comma) {
54679                break;
54680            }
54681        }
54682
54683        self.expect(TokenType::RParen)?;
54684
54685        // Check for WITH ORDINALITY
54686        let ordinality =
54687            if self.match_token(TokenType::With) && self.match_token(TokenType::Ordinality) {
54688                true
54689            } else {
54690                false
54691            };
54692
54693        // Check for outer alias: AS alias(col1 type1, col2 type2, ...)
54694        let alias = if self.match_token(TokenType::As) {
54695            Some(Box::new(self.parse_rows_from_alias()?))
54696        } else {
54697            None
54698        };
54699
54700        Ok(Expression::RowsFrom(Box::new(RowsFrom {
54701            expressions,
54702            ordinality,
54703            alias,
54704        })))
54705    }
54706
54707    /// Parse a single function in ROWS FROM: func_name(args) [AS alias(col1 type1, ...)]
54708    fn parse_rows_from_function(&mut self) -> Result<Expression> {
54709        // Parse function name
54710        let func_name = self.expect_identifier_or_keyword()?;
54711
54712        // Parse function arguments
54713        self.expect(TokenType::LParen)?;
54714        let args = if self.check(TokenType::RParen) {
54715            Vec::new()
54716        } else {
54717            self.parse_function_arguments()?
54718        };
54719        self.expect(TokenType::RParen)?;
54720
54721        let func_expr = Expression::Function(Box::new(Function {
54722            name: func_name,
54723            args,
54724            distinct: false,
54725            trailing_comments: Vec::new(),
54726            use_bracket_syntax: false,
54727            no_parens: false,
54728            quoted: false,
54729            span: None,
54730            inferred_type: None,
54731        }));
54732
54733        // Check for AS alias(col1 type1, col2 type2, ...)
54734        // Return a Tuple(function, TableAlias) so the generator can output: FUNC() AS alias(col type)
54735        if self.match_token(TokenType::As) {
54736            let alias_expr = self.parse_rows_from_alias()?;
54737            Ok(Expression::Tuple(Box::new(Tuple {
54738                expressions: vec![func_expr, alias_expr],
54739            })))
54740        } else {
54741            Ok(func_expr)
54742        }
54743    }
54744
54745    /// Parse ROWS FROM alias with typed columns: alias_name(col1 type1, col2 type2, ...)
54746    fn parse_rows_from_alias(&mut self) -> Result<Expression> {
54747        let alias_name = self.expect_identifier_or_keyword_with_quoted()?;
54748
54749        // Check for column definitions: (col1 type1, col2 type2, ...)
54750        let columns = if self.match_token(TokenType::LParen) {
54751            let mut cols = Vec::new();
54752            loop {
54753                if self.check(TokenType::RParen) {
54754                    break;
54755                }
54756                // Parse column name (can be quoted)
54757                let col_name = self.expect_identifier_or_keyword_with_quoted()?;
54758                // Parse column type
54759                let col_type = self.parse_data_type()?;
54760                // Create ColumnDef expression, preserving the quoted status
54761                let mut col_def = ColumnDef::new(col_name.name.clone(), col_type);
54762                col_def.name = col_name; // Preserve the full identifier with quoted flag
54763                cols.push(Expression::ColumnDef(Box::new(col_def)));
54764
54765                if !self.match_token(TokenType::Comma) {
54766                    break;
54767                }
54768            }
54769            self.expect(TokenType::RParen)?;
54770            cols
54771        } else {
54772            Vec::new()
54773        };
54774
54775        Ok(Expression::TableAlias(Box::new(TableAlias {
54776            this: Some(Box::new(Expression::Identifier(alias_name))),
54777            columns,
54778        })))
54779    }
54780
54781    /// parse_table - Implemented from Python _parse_table
54782    /// Calls: parse_table_hints, parse_unnest, parse_partition
54783    #[allow(unused_variables, unused_mut)]
54784    pub fn parse_table(&mut self) -> Result<Option<Expression>> {
54785        if self.match_text_seq(&["ROWS", "FROM"]) {
54786            // ROWS FROM is handled by parse_rows_from() in parse_table_expression()
54787            return Ok(None);
54788        }
54789        if self.match_text_seq(&["*"]) {
54790            // Matched: *
54791            return Ok(None);
54792        }
54793        if self.match_text_seq(&["NOT", "INDEXED"]) {
54794            // Matched: NOT INDEXED
54795            return Ok(None);
54796        }
54797        Ok(None)
54798    }
54799
54800    /// parse_table_alias - Ported from Python _parse_table_alias
54801    /// Parses table alias: AS alias [(col1, col2, ...)]
54802    #[allow(unused_variables, unused_mut)]
54803    pub fn parse_table_alias(&mut self) -> Result<Option<Expression>> {
54804        // Check for AS keyword (optional in most dialects)
54805        let has_as = self.match_token(TokenType::As);
54806
54807        // Handle AS (col1, col2) - no alias name, just column aliases
54808        if has_as && self.check(TokenType::LParen) {
54809            // Parse (col1, col2, ...)
54810            self.skip(); // consume LParen
54811            let mut cols = Vec::new();
54812            loop {
54813                if self.check(TokenType::RParen) {
54814                    break;
54815                }
54816                if let Ok(Some(col)) = self.parse_id_var() {
54817                    cols.push(col);
54818                }
54819                if !self.match_token(TokenType::Comma) {
54820                    break;
54821                }
54822            }
54823            self.expect(TokenType::RParen)?;
54824            return Ok(Some(Expression::TableAlias(Box::new(TableAlias {
54825                this: None,
54826                columns: cols,
54827            }))));
54828        }
54829
54830        // Parse the alias identifier
54831        // ClickHouse: keywords can be used as table aliases (e.g., AS select, AS from)
54832        let is_keyword_alias = has_as
54833            && matches!(
54834                self.config.dialect,
54835                Some(crate::dialects::DialectType::ClickHouse)
54836            )
54837            && self.peek().token_type.is_keyword();
54838        if !self.check(TokenType::Identifier)
54839            && !self.check(TokenType::QuotedIdentifier)
54840            && !self.check(TokenType::Var)
54841            && !is_keyword_alias
54842        {
54843            if has_as {
54844                return Err(self.parse_error("Expected identifier after AS"));
54845            }
54846            return Ok(None);
54847        }
54848
54849        let alias_token = self.advance();
54850        let is_quoted = alias_token.token_type == TokenType::QuotedIdentifier;
54851        let mut alias_ident = Identifier::new(alias_token.text.clone());
54852        if is_quoted {
54853            alias_ident.quoted = true;
54854        }
54855        let alias = Expression::Identifier(alias_ident);
54856
54857        // Check for column list: (col1, col2, ...)
54858        let columns = if self.match_token(TokenType::LParen) {
54859            let mut cols = Vec::new();
54860            loop {
54861                if self.check(TokenType::RParen) {
54862                    break;
54863                }
54864                if let Ok(Some(col)) = self.parse_id_var() {
54865                    cols.push(col);
54866                }
54867                if !self.match_token(TokenType::Comma) {
54868                    break;
54869                }
54870            }
54871            self.expect(TokenType::RParen)?;
54872            cols
54873        } else {
54874            Vec::new()
54875        };
54876
54877        Ok(Some(Expression::TableAlias(Box::new(TableAlias {
54878            this: Some(Box::new(alias)),
54879            columns,
54880        }))))
54881    }
54882
54883    /// parse_table_hints - Ported from Python _parse_table_hints
54884    /// Parses table hints (SQL Server WITH (...) or MySQL USE/IGNORE/FORCE INDEX)
54885    #[allow(unused_variables, unused_mut)]
54886    pub fn parse_table_hints(&mut self) -> Result<Option<Expression>> {
54887        let mut hints = Vec::new();
54888
54889        // SQL Server style: WITH (hint1, hint2, ...)
54890        if self.match_text_seq(&["WITH"]) && self.match_token(TokenType::LParen) {
54891            let mut expressions = Vec::new();
54892            loop {
54893                // Parse function or variable as hint
54894                if let Some(func) = self.parse_function()? {
54895                    expressions.push(func);
54896                } else if let Some(var) = self.parse_var()? {
54897                    expressions.push(var);
54898                } else {
54899                    break;
54900                }
54901                if !self.match_token(TokenType::Comma) {
54902                    break;
54903                }
54904            }
54905            self.match_token(TokenType::RParen);
54906
54907            if !expressions.is_empty() {
54908                hints.push(Expression::WithTableHint(Box::new(WithTableHint {
54909                    expressions,
54910                })));
54911            }
54912        } else {
54913            // MySQL style: USE INDEX, IGNORE INDEX, FORCE INDEX
54914            while self.match_texts(&["USE", "IGNORE", "FORCE"]) {
54915                let hint_type = self.previous().text.to_ascii_uppercase();
54916
54917                // Match INDEX or KEY
54918                let _ = self.match_texts(&["INDEX", "KEY"]);
54919
54920                // Check for optional FOR clause: FOR JOIN, FOR ORDER BY, FOR GROUP BY
54921                let target = if self.match_text_seq(&["FOR"]) {
54922                    let target_token = self.advance();
54923                    let target_text = target_token.text.to_ascii_uppercase();
54924                    // For ORDER BY and GROUP BY, combine into a single target name
54925                    let full_target = if (target_text == "ORDER" || target_text == "GROUP")
54926                        && self.check(TokenType::By)
54927                    {
54928                        self.skip(); // consume BY
54929                        format!("{} BY", target_text)
54930                    } else {
54931                        target_text
54932                    };
54933                    Some(Box::new(Expression::Identifier(Identifier {
54934                        name: full_target,
54935                        quoted: false,
54936                        trailing_comments: Vec::new(),
54937                        span: None,
54938                    })))
54939                } else {
54940                    None
54941                };
54942
54943                // Parse wrapped identifiers (index names — can include keywords like PRIMARY)
54944                let expressions = if self.match_token(TokenType::LParen) {
54945                    let mut ids = Vec::new();
54946                    loop {
54947                        if self.check(TokenType::RParen) {
54948                            break;
54949                        }
54950                        if let Some(id) = self.parse_id_var()? {
54951                            ids.push(id);
54952                        } else if self.is_safe_keyword_as_identifier()
54953                            || self.check(TokenType::PrimaryKey)
54954                        {
54955                            // Accept keywords as index names (e.g., PRIMARY)
54956                            let name = self.advance().text.clone();
54957                            ids.push(Expression::Identifier(Identifier::new(name)));
54958                        } else {
54959                            break;
54960                        }
54961                        if !self.match_token(TokenType::Comma) {
54962                            break;
54963                        }
54964                    }
54965                    self.match_token(TokenType::RParen);
54966                    ids
54967                } else {
54968                    Vec::new()
54969                };
54970
54971                hints.push(Expression::IndexTableHint(Box::new(IndexTableHint {
54972                    this: Box::new(Expression::Identifier(Identifier {
54973                        name: hint_type,
54974                        quoted: false,
54975                        trailing_comments: Vec::new(),
54976                        span: None,
54977                    })),
54978                    expressions,
54979                    target,
54980                })));
54981            }
54982        }
54983
54984        if hints.is_empty() {
54985            return Ok(None);
54986        }
54987
54988        // Return as a Tuple containing hints
54989        Ok(Some(Expression::Tuple(Box::new(Tuple {
54990            expressions: hints,
54991        }))))
54992    }
54993
54994    /// Parse TSQL TRUNCATE table hints: WITH (PARTITIONS(1, 2 TO 5, 10 TO 20, 84))
54995    /// Unlike regular table hints, PARTITIONS arguments can contain TO ranges.
54996    pub fn parse_truncate_table_hints(&mut self) -> Result<Option<Expression>> {
54997        if !self.match_text_seq(&["WITH"]) || !self.match_token(TokenType::LParen) {
54998            return Ok(None);
54999        }
55000
55001        let mut hints = Vec::new();
55002
55003        // Check for PARTITIONS specifically
55004        if self.check_identifier("PARTITIONS") {
55005            self.skip(); // consume PARTITIONS
55006            self.expect(TokenType::LParen)?;
55007
55008            // Parse partition ranges: 1, 2 TO 5, 10 TO 20, 84
55009            let mut parts = Vec::new();
55010            loop {
55011                if self.check(TokenType::RParen) {
55012                    break;
55013                }
55014                let low = self.parse_primary()?;
55015                if self.match_text_seq(&["TO"]) {
55016                    let high = self.parse_primary()?;
55017                    parts.push(Expression::PartitionRange(Box::new(PartitionRange {
55018                        this: Box::new(low),
55019                        expression: Some(Box::new(high)),
55020                        expressions: Vec::new(),
55021                    })));
55022                } else {
55023                    parts.push(low);
55024                }
55025                if !self.match_token(TokenType::Comma) {
55026                    break;
55027                }
55028            }
55029            self.expect(TokenType::RParen)?; // close PARTITIONS(...)
55030
55031            // Create an Anonymous function for PARTITIONS(...)
55032            hints.push(Expression::Anonymous(Box::new(Anonymous {
55033                this: Box::new(Expression::Identifier(Identifier {
55034                    name: "PARTITIONS".to_string(),
55035                    quoted: false,
55036                    trailing_comments: Vec::new(),
55037                    span: None,
55038                })),
55039                expressions: parts,
55040            })));
55041        } else {
55042            // Fall back to regular hint parsing (function or var)
55043            loop {
55044                if let Some(func) = self.parse_function()? {
55045                    hints.push(func);
55046                } else if let Some(var) = self.parse_var()? {
55047                    hints.push(var);
55048                } else {
55049                    break;
55050                }
55051                if !self.match_token(TokenType::Comma) {
55052                    break;
55053                }
55054            }
55055        }
55056
55057        self.expect(TokenType::RParen)?; // close WITH(...)
55058
55059        if hints.is_empty() {
55060            return Ok(None);
55061        }
55062
55063        // Wrap in WithTableHint then Tuple (same as parse_table_hints)
55064        let hint = Expression::WithTableHint(Box::new(WithTableHint { expressions: hints }));
55065
55066        Ok(Some(Expression::Tuple(Box::new(Tuple {
55067            expressions: vec![hint],
55068        }))))
55069    }
55070
55071    /// parse_table_part - Parse a single part of a table reference
55072    /// Tries: identifier, string as identifier, placeholder
55073    #[allow(unused_variables, unused_mut)]
55074    pub fn parse_table_part(&mut self) -> Result<Option<Expression>> {
55075        // Try to parse an identifier
55076        if let Some(id) = self.parse_id_var()? {
55077            return Ok(Some(id));
55078        }
55079
55080        // Try to parse a string as identifier
55081        if let Some(str_id) = self.parse_string_as_identifier()? {
55082            return Ok(Some(str_id));
55083        }
55084
55085        // Try to parse a placeholder
55086        if let Some(placeholder) = self.parse_placeholder()? {
55087            return Ok(Some(placeholder));
55088        }
55089
55090        // Accept keywords as identifiers in table part context (e.g., db.cluster where "cluster" is a keyword)
55091        // This mirrors Python sqlglot's ID_VAR_TOKENS which includes many keyword types
55092        if self.check_keyword_as_identifier() {
55093            let text = self.peek().text.clone();
55094            self.skip();
55095            return Ok(Some(Expression::Identifier(Identifier {
55096                name: text,
55097                quoted: false,
55098                trailing_comments: Vec::new(),
55099                span: None,
55100            })));
55101        }
55102
55103        Ok(None)
55104    }
55105
55106    /// Check if the current token is a keyword that can be used as an identifier in certain contexts
55107    /// This includes many SQL keywords like CLUSTER, TABLE, INDEX, etc.
55108    fn check_keyword_as_identifier(&self) -> bool {
55109        if self.is_at_end() {
55110            return false;
55111        }
55112        let token_type = self.peek().token_type;
55113        // Keywords that can be used as identifiers (similar to Python's ID_VAR_TOKENS)
55114        matches!(
55115            token_type,
55116            TokenType::Cluster
55117                | TokenType::Table
55118                | TokenType::Index
55119                | TokenType::View
55120                | TokenType::Database
55121                | TokenType::Schema
55122                | TokenType::Column
55123                | TokenType::Function
55124                | TokenType::Procedure
55125                | TokenType::Constraint
55126                | TokenType::Sequence
55127                | TokenType::Type
55128                | TokenType::Partition
55129                | TokenType::Comment
55130                | TokenType::Cache
55131                | TokenType::Commit
55132                | TokenType::Begin
55133                | TokenType::End
55134                | TokenType::Set
55135                | TokenType::Show
55136                | TokenType::Describe
55137                | TokenType::Use
55138                | TokenType::Execute
55139                | TokenType::Delete
55140                | TokenType::Update
55141                | TokenType::Merge
55142                | TokenType::Load
55143                | TokenType::Copy
55144                | TokenType::Truncate
55145                | TokenType::Replace
55146                | TokenType::Refresh
55147                | TokenType::Rename
55148                | TokenType::Filter
55149                | TokenType::Format
55150                | TokenType::First
55151                | TokenType::Next
55152                | TokenType::Last
55153                | TokenType::Keep
55154                | TokenType::Match
55155                | TokenType::Over
55156                | TokenType::Range
55157                | TokenType::Rows
55158                | TokenType::Row
55159                | TokenType::Offset
55160                | TokenType::Limit
55161                | TokenType::Top
55162                | TokenType::Cube
55163                | TokenType::Rollup
55164                | TokenType::Pivot
55165                | TokenType::Unpivot
55166                | TokenType::Window
55167                | TokenType::Recursive
55168                | TokenType::Unique
55169                | TokenType::Temporary
55170                | TokenType::Volatile
55171                | TokenType::References
55172                | TokenType::Natural
55173                | TokenType::Left
55174                | TokenType::Right
55175                | TokenType::Full
55176                | TokenType::Semi
55177                | TokenType::Anti
55178                | TokenType::Apply
55179                | TokenType::All
55180                | TokenType::Asc
55181                | TokenType::Desc
55182                | TokenType::Analyze
55183        )
55184    }
55185
55186    /// parse_table_parts - Parse catalog.schema.table or schema.table or table
55187    /// Returns a Table expression with all parts
55188    #[allow(unused_variables, unused_mut)]
55189    pub fn parse_table_parts(&mut self) -> Result<Option<Expression>> {
55190        // Parse the first part
55191        let first = self.parse_table_part()?;
55192        if first.is_none() {
55193            return Ok(None);
55194        }
55195
55196        let mut parts = vec![first.unwrap()];
55197
55198        // Parse additional dot-separated parts
55199        while self.match_token(TokenType::Dot) {
55200            if let Some(part) = self.parse_table_part()? {
55201                parts.push(part);
55202            } else {
55203                break;
55204            }
55205        }
55206
55207        // Convert parts to Table expression
55208        // Last part is table name, second-to-last is schema, third-to-last is catalog
55209        let (catalog, schema, name) = match parts.len() {
55210            1 => (None, None, parts.pop().unwrap()),
55211            2 => {
55212                let table = parts.pop().unwrap();
55213                let schema = parts.pop().unwrap();
55214                (None, Some(schema), table)
55215            }
55216            _ => {
55217                let table = parts.pop().unwrap();
55218                let schema = parts.pop().unwrap();
55219                let catalog = parts.pop();
55220                (catalog, Some(schema), table)
55221            }
55222        };
55223
55224        // Extract identifier from Expression
55225        let name_ident = match name {
55226            Expression::Identifier(id) => id,
55227            _ => Identifier::new(String::new()),
55228        };
55229        let schema_ident = schema.map(|s| match s {
55230            Expression::Identifier(id) => id,
55231            _ => Identifier::new(String::new()),
55232        });
55233        let catalog_ident = catalog.map(|c| match c {
55234            Expression::Identifier(id) => id,
55235            _ => Identifier::new(String::new()),
55236        });
55237
55238        Ok(Some(Expression::boxed_table(TableRef {
55239            name: name_ident,
55240            schema: schema_ident,
55241            catalog: catalog_ident,
55242            alias: None,
55243            alias_explicit_as: false,
55244            column_aliases: Vec::new(),
55245            leading_comments: Vec::new(),
55246            trailing_comments: Vec::new(),
55247            when: None,
55248            only: false,
55249            final_: false,
55250            table_sample: None,
55251            hints: Vec::new(),
55252            system_time: None,
55253            partitions: Vec::new(),
55254            identifier_func: None,
55255            changes: None,
55256            version: None,
55257            span: None,
55258        })))
55259    }
55260
55261    /// parse_table_sample - Implemented from Python _parse_table_sample
55262    /// Calls: parse_number, parse_factor, parse_placeholder
55263    #[allow(unused_variables, unused_mut)]
55264    pub fn parse_table_sample(&mut self) -> Result<Option<Expression>> {
55265        if self.match_text_seq(&["USING", "SAMPLE"]) {
55266            return Ok(Some(Expression::TableSample(Box::new(TableSample {
55267                this: None,
55268                sample: None,
55269                expressions: Vec::new(),
55270                method: None,
55271                bucket_numerator: None,
55272                bucket_denominator: None,
55273                bucket_field: None,
55274                percent: None,
55275                rows: None,
55276                size: None,
55277                seed: None,
55278            }))));
55279        }
55280        if self.match_text_seq(&["BUCKET"]) {
55281            // Matched: BUCKET
55282            return Ok(None);
55283        }
55284        if self.match_text_seq(&["OUT", "OF"]) {
55285            // Matched: OUT OF
55286            return Ok(None);
55287        }
55288        if self.match_texts(&["SEED", "REPEATABLE"]) {
55289            // Matched one of: SEED, REPEATABLE
55290            return Ok(None);
55291        }
55292        Ok(None)
55293    }
55294
55295    /// parse_term - Parses addition/subtraction expressions (+ - operators)
55296    /// Python: _parse_term
55297    /// Delegates to the existing parse_addition in the operator precedence chain
55298    pub fn parse_term(&mut self) -> Result<Option<Expression>> {
55299        // Delegate to the existing addition parsing
55300        match self.parse_addition() {
55301            Ok(expr) => Ok(Some(expr)),
55302            Err(_) => Ok(None),
55303        }
55304    }
55305
55306    /// parse_to_table - ClickHouse TO table property
55307    /// Parses: TO table_name
55308    #[allow(unused_variables, unused_mut)]
55309    pub fn parse_to_table(&mut self) -> Result<Option<Expression>> {
55310        // Parse the table reference
55311        let table = self.parse_table_parts()?;
55312        if table.is_none() {
55313            return Ok(None);
55314        }
55315
55316        Ok(Some(Expression::ToTableProperty(Box::new(
55317            ToTableProperty {
55318                this: Box::new(table.unwrap()),
55319            },
55320        ))))
55321    }
55322
55323    /// parse_tokens - Operator precedence parser
55324    #[allow(unused_variables, unused_mut)]
55325    pub fn parse_tokens(&mut self) -> Result<Option<Expression>> {
55326        // Uses operator precedence parsing pattern
55327        Ok(None)
55328    }
55329
55330    /// parse_trim - Ported from Python _parse_trim
55331    /// Parses TRIM function: TRIM([BOTH|LEADING|TRAILING] chars FROM str) or TRIM(str, chars)
55332    #[allow(unused_variables, unused_mut)]
55333    pub fn parse_trim(&mut self) -> Result<Option<Expression>> {
55334        // Check for position keyword (BOTH, LEADING, TRAILING)
55335        let (position, position_explicit) = if self.match_texts(&["BOTH"]) {
55336            (TrimPosition::Both, true)
55337        } else if self.match_texts(&["LEADING"]) {
55338            (TrimPosition::Leading, true)
55339        } else if self.match_texts(&["TRAILING"]) {
55340            (TrimPosition::Trailing, true)
55341        } else {
55342            (TrimPosition::Both, false)
55343        };
55344
55345        // Parse first expression
55346        let first = match self.parse_bitwise() {
55347            Ok(Some(expr)) => self.try_clickhouse_func_arg_alias(expr),
55348            Ok(None) => return Ok(None),
55349            Err(e) => return Err(e),
55350        };
55351
55352        // Check for FROM or comma to see if there's a second expression
55353        let (this, characters, sql_standard_syntax) = if self.match_token(TokenType::From) {
55354            // SQL standard syntax: TRIM([position] chars FROM str)
55355            let second = match self.parse_bitwise() {
55356                Ok(Some(expr)) => self.try_clickhouse_func_arg_alias(expr),
55357                Ok(None) => return Err(self.parse_error("Expected expression after FROM in TRIM")),
55358                Err(e) => return Err(e),
55359            };
55360            // In SQL standard syntax: first is characters, second is the string
55361            (second, Some(first), true)
55362        } else if self.match_token(TokenType::Comma) {
55363            // Function syntax: TRIM(a, b)
55364            let second = match self.parse_bitwise() {
55365                Ok(Some(expr)) => Some(expr),
55366                Ok(None) => None,
55367                Err(e) => return Err(e),
55368            };
55369            // In Spark, comma syntax is TRIM(chars, str) - pattern first
55370            // In other dialects, comma syntax is TRIM(str, chars) - string first
55371            let trim_pattern_first = matches!(
55372                self.config.dialect,
55373                Some(crate::dialects::DialectType::Spark)
55374            );
55375            if trim_pattern_first && second.is_some() {
55376                // first=chars, second=str
55377                (second.unwrap(), Some(first), false)
55378            } else {
55379                (first, second, false)
55380            }
55381        } else {
55382            // Single argument: TRIM(str)
55383            (first, None, false)
55384        };
55385
55386        Ok(Some(Expression::Trim(Box::new(TrimFunc {
55387            this,
55388            characters,
55389            position,
55390            sql_standard_syntax,
55391            position_explicit,
55392        }))))
55393    }
55394
55395    /// parse_truncate_table - Implemented from Python _parse_truncate_table
55396    /// Calls: parse_on_property, parse_partition, parse_function
55397    #[allow(unused_variables, unused_mut)]
55398    pub fn parse_truncate_table(&mut self) -> Result<Option<Expression>> {
55399        if self.match_text_seq(&["RESTART", "IDENTITY"]) {
55400            return Ok(Some(Expression::TruncateTable(Box::new(TruncateTable {
55401                expressions: Vec::new(),
55402                is_database: None,
55403                exists: false,
55404                only: None,
55405                cluster: None,
55406                identity: None,
55407                option: None,
55408                partition: None,
55409            }))));
55410        }
55411        if self.match_text_seq(&["CONTINUE", "IDENTITY"]) {
55412            // Matched: CONTINUE IDENTITY
55413            return Ok(None);
55414        }
55415        if self.match_text_seq(&["CASCADE"]) {
55416            // Matched: CASCADE
55417            return Ok(None);
55418        }
55419        Ok(None)
55420    }
55421
55422    /// parse_ttl - Implemented from Python _parse_ttl
55423    /// Parses ClickHouse TTL expression with optional DELETE, RECOMPRESS, TO DISK/VOLUME
55424    pub fn parse_ttl(&mut self) -> Result<Option<Expression>> {
55425        // Parse CSV of TTL actions
55426        let mut expressions = Vec::new();
55427
55428        loop {
55429            // Parse the base expression
55430            let base_start = self.current;
55431            let this = match self.parse_bitwise() {
55432                Ok(Some(expr)) => expr,
55433                _ => {
55434                    self.current = base_start;
55435                    let mut paren_depth = 0usize;
55436                    while !self.is_at_end() {
55437                        if paren_depth == 0
55438                            && (self.check(TokenType::Comma)
55439                                || self.peek().text.eq_ignore_ascii_case("DELETE")
55440                                || self.peek().text.eq_ignore_ascii_case("RECOMPRESS")
55441                                || self.peek().text.eq_ignore_ascii_case("TO")
55442                                || self.peek().text.eq_ignore_ascii_case("WHERE")
55443                                || self.peek().text.eq_ignore_ascii_case("GROUP")
55444                                || self.peek().text.eq_ignore_ascii_case("SET"))
55445                        {
55446                            break;
55447                        }
55448                        if self.check(TokenType::LParen) {
55449                            paren_depth += 1;
55450                        } else if self.check(TokenType::RParen) {
55451                            if paren_depth == 0 {
55452                                break;
55453                            }
55454                            paren_depth -= 1;
55455                        }
55456                        self.skip();
55457                    }
55458                    if self.current == base_start {
55459                        break;
55460                    }
55461                    let raw = self
55462                        .tokens_to_sql(base_start, self.current)
55463                        .trim()
55464                        .to_string();
55465                    Expression::Var(Box::new(Var { this: raw }))
55466                }
55467            };
55468
55469            // Check for TTL action
55470            let action = if self.match_text_seq(&["DELETE"]) {
55471                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
55472                    this: Box::new(this),
55473                    delete: Some(Box::new(Expression::Boolean(BooleanLiteral {
55474                        value: true,
55475                    }))),
55476                    recompress: None,
55477                    to_disk: None,
55478                    to_volume: None,
55479                }))
55480            } else if self.match_text_seq(&["RECOMPRESS"]) {
55481                let recompress = if self.match_identifier("CODEC") {
55482                    self.expect(TokenType::LParen)?;
55483                    let mut args = Vec::new();
55484                    if !self.check(TokenType::RParen) {
55485                        args.push(self.parse_expression()?);
55486                        while self.match_token(TokenType::Comma) {
55487                            args.push(self.parse_expression()?);
55488                        }
55489                    }
55490                    self.expect(TokenType::RParen)?;
55491                    Some(Box::new(Expression::Function(Box::new(Function::new(
55492                        "CODEC".to_string(),
55493                        args,
55494                    )))))
55495                } else {
55496                    self.parse_bitwise()?.map(Box::new)
55497                };
55498                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
55499                    this: Box::new(this),
55500                    delete: None,
55501                    recompress,
55502                    to_disk: None,
55503                    to_volume: None,
55504                }))
55505            } else if self.match_text_seq(&["TO", "DISK"]) {
55506                let to_disk = self.parse_string()?.map(Box::new);
55507                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
55508                    this: Box::new(this),
55509                    delete: None,
55510                    recompress: None,
55511                    to_disk,
55512                    to_volume: None,
55513                }))
55514            } else if self.match_text_seq(&["TO", "VOLUME"]) {
55515                let to_volume = self.parse_string()?.map(Box::new);
55516                Expression::MergeTreeTTLAction(Box::new(MergeTreeTTLAction {
55517                    this: Box::new(this),
55518                    delete: None,
55519                    recompress: None,
55520                    to_disk: None,
55521                    to_volume,
55522                }))
55523            } else {
55524                this
55525            };
55526
55527            expressions.push(action);
55528
55529            if !self.match_token(TokenType::Comma) {
55530                break;
55531            }
55532        }
55533
55534        // Parse optional top-level WHERE clause (for backwards compatibility)
55535        let where_ = self.parse_where()?.map(Box::new);
55536
55537        // Parse optional GROUP BY
55538        let group = if self.match_token(TokenType::Group) {
55539            self.expect(TokenType::By)?;
55540            let mut exprs = Vec::new();
55541            exprs.push(self.parse_expression()?);
55542            while self.match_token(TokenType::Comma) {
55543                exprs.push(self.parse_expression()?);
55544            }
55545            Some(Box::new(Expression::Group(Box::new(Group {
55546                expressions: exprs,
55547                grouping_sets: None,
55548                cube: None,
55549                rollup: None,
55550                totals: None,
55551                all: None,
55552            }))))
55553        } else {
55554            None
55555        };
55556
55557        // Parse optional SET (aggregates) after GROUP BY
55558        let aggregates = if group.is_some() && self.match_token(TokenType::Set) {
55559            let mut aggs = Vec::new();
55560            loop {
55561                aggs.push(self.parse_expression()?);
55562                if !self.match_token(TokenType::Comma) {
55563                    break;
55564                }
55565            }
55566            if aggs.is_empty() {
55567                None
55568            } else {
55569                Some(Box::new(Expression::Tuple(Box::new(Tuple {
55570                    expressions: aggs,
55571                }))))
55572            }
55573        } else {
55574            None
55575        };
55576
55577        Ok(Some(Expression::MergeTreeTTL(Box::new(MergeTreeTTL {
55578            expressions,
55579            where_,
55580            group,
55581            aggregates,
55582        }))))
55583    }
55584
55585    /// parse_type - Parses a data type expression
55586    /// Python: _parse_type
55587    pub fn parse_type(&mut self) -> Result<Option<Expression>> {
55588        // First try to parse an interval
55589        if let Some(interval) = self.parse_interval()? {
55590            return self.parse_column_ops_with_expr(Some(interval));
55591        }
55592
55593        // Try to parse a data type
55594        let data_type = self.parse_types()?;
55595
55596        if let Some(dt) = data_type {
55597            // If it's a Cast (BigQuery inline constructor), apply column ops
55598            if matches!(dt, Expression::Cast(_)) {
55599                return self.parse_column_ops_with_expr(Some(dt));
55600            }
55601
55602            // Try to parse a primary expression after the type
55603            let start_pos = self.current;
55604            if let Some(primary) = self.parse_primary_or_var()? {
55605                // If it's a literal, this might be a type cast like DATE '2020-01-01'
55606                if let Expression::Literal(_) = &primary {
55607                    let result = self.parse_column_ops_with_expr(Some(primary))?;
55608                    if let Some(value) = result {
55609                        // Create a Cast expression
55610                        if let Expression::DataType(data_type_struct) = dt {
55611                            return Ok(Some(Expression::Cast(Box::new(Cast {
55612                                this: value,
55613                                to: data_type_struct,
55614                                trailing_comments: Vec::new(),
55615                                double_colon_syntax: false,
55616                                format: None,
55617                                default: None,
55618                                inferred_type: None,
55619                            }))));
55620                        }
55621                    }
55622                }
55623                // Backtrack if not a type-literal pattern
55624                self.current = start_pos;
55625            }
55626
55627            return Ok(Some(dt));
55628        }
55629
55630        Ok(None)
55631    }
55632
55633    /// parse_type_size - Ported from Python _parse_type_size
55634    /// Parses type size parameters like 10 in VARCHAR(10) or 10, 2 in DECIMAL(10, 2)
55635    #[allow(unused_variables, unused_mut)]
55636    pub fn parse_type_size(&mut self) -> Result<Option<Expression>> {
55637        // First try to parse a type - this handles both numeric literals and type names
55638        let this = self.parse_type()?;
55639
55640        if this.is_none() {
55641            return Ok(None);
55642        }
55643
55644        let mut result = this.unwrap();
55645
55646        // If it's a Column with no table, convert it to an Identifier (var)
55647        // This handles cases like CHAR in VARCHAR(100 CHAR)
55648        if let Expression::Column(ref col) = result {
55649            if col.table.is_none() {
55650                result = Expression::Identifier(col.name.clone());
55651            }
55652        }
55653
55654        // Check for optional expression after the type (e.g., "CHAR" in "100 CHAR")
55655        // This is for byte/char length specifiers in some dialects
55656        if let Some(var_token) = self.parse_var()? {
55657            // We have an additional specifier, combine them
55658            // For now, just return the original result since Rust doesn't have DataTypeParam
55659            // The var expression would be attached as an expression in Python
55660        }
55661
55662        Ok(Some(result))
55663    }
55664
55665    /// parse_types - Implemented from Python _parse_types
55666    /// Calls: parse_string
55667    #[allow(unused_variables, unused_mut)]
55668    pub fn parse_types(&mut self) -> Result<Option<Expression>> {
55669        if self.match_text_seq(&["SYSUDTLIB", "."]) {
55670            return Ok(Some(Expression::Identifier(Identifier {
55671                name: String::new(),
55672                quoted: false,
55673                trailing_comments: Vec::new(),
55674                span: None,
55675            })));
55676        }
55677        if self.match_text_seq(&["WITH", "TIME", "ZONE"]) {
55678            // Matched: WITH TIME ZONE
55679            return Ok(None);
55680        }
55681        if self.match_text_seq(&["WITH", "LOCAL", "TIME", "ZONE"]) {
55682            // Matched: WITH LOCAL TIME ZONE
55683            return Ok(None);
55684        }
55685        Ok(None)
55686    }
55687
55688    /// parse_unique - Implemented from Python _parse_unique
55689    /// Parses UNIQUE [KEY|INDEX] [NULLS NOT DISTINCT] [(columns)] [USING index_type]
55690    #[allow(unused_variables, unused_mut)]
55691    pub fn parse_unique(&mut self) -> Result<Option<Expression>> {
55692        // Check for optional KEY/INDEX
55693        let _ = self.match_texts(&["KEY", "INDEX"]);
55694
55695        // Check for NULLS NOT DISTINCT (PostgreSQL 15+ feature)
55696        let nulls = if self.match_text_seq(&["NULLS", "NOT", "DISTINCT"]) {
55697            Some(Box::new(Expression::Boolean(BooleanLiteral {
55698                value: true,
55699            })))
55700        } else {
55701            None
55702        };
55703
55704        // Parse the optional key name and schema (column list)
55705        let unique_key = self.parse_unique_key()?;
55706        let this = self.parse_schema_with_this(unique_key)?;
55707
55708        // Parse optional USING index_type
55709        let index_type = if self.match_token(TokenType::Using) {
55710            self.skip();
55711            Some(Box::new(Expression::Var(Box::new(Var {
55712                this: self.previous().text.clone(),
55713            }))))
55714        } else {
55715            None
55716        };
55717
55718        Ok(Some(Expression::UniqueColumnConstraint(Box::new(
55719            UniqueColumnConstraint {
55720                this: this.map(Box::new),
55721                index_type,
55722                on_conflict: None,
55723                nulls,
55724                options: Vec::new(),
55725            },
55726        ))))
55727    }
55728
55729    /// parse_unique_key - Parse the key/index name for UNIQUE constraint
55730    /// Simply parses an identifier
55731    #[allow(unused_variables, unused_mut)]
55732    pub fn parse_unique_key(&mut self) -> Result<Option<Expression>> {
55733        self.parse_id_var()
55734    }
55735
55736    /// parse_unnest - Ported from Python _parse_unnest
55737    /// Parses UNNEST(array_expr) [WITH ORDINALITY] [AS alias]
55738    #[allow(unused_variables, unused_mut)]
55739    pub fn parse_unnest(&mut self) -> Result<Option<Expression>> {
55740        // Check for UNNEST keyword
55741        if !self.match_texts(&["UNNEST"]) {
55742            return Ok(None);
55743        }
55744
55745        // Expect opening parenthesis
55746        if !self.match_token(TokenType::LParen) {
55747            return Ok(None);
55748        }
55749
55750        // Parse comma-separated array expression(s): UNNEST(arr1, arr2, ...)
55751        let this = match self.parse_expression() {
55752            Ok(expr) => expr,
55753            Err(e) => return Err(e),
55754        };
55755
55756        let mut extra_expressions = Vec::new();
55757        while self.match_token(TokenType::Comma) {
55758            let expr = self.parse_expression()?;
55759            extra_expressions.push(expr);
55760        }
55761
55762        // Expect closing parenthesis
55763        self.expect(TokenType::RParen)?;
55764
55765        // Check for WITH ORDINALITY (Presto) or WITH OFFSET (BigQuery)
55766        let mut with_ordinality = self.match_text_seq(&["WITH", "ORDINALITY"]);
55767        let mut offset_alias = None;
55768        if !with_ordinality && self.match_text_seq(&["WITH", "OFFSET"]) {
55769            with_ordinality = true;
55770            // Parse optional offset alias: WITH OFFSET AS y or WITH OFFSET y
55771            if matches!(
55772                self.config.dialect,
55773                Some(crate::dialects::DialectType::BigQuery)
55774            ) {
55775                let has_as = self.match_token(TokenType::As);
55776                if has_as || self.check(TokenType::Identifier) || self.check(TokenType::Var) {
55777                    let alias_name = self.advance().text;
55778                    offset_alias = Some(crate::expressions::Identifier {
55779                        name: alias_name,
55780                        quoted: false,
55781                        trailing_comments: Vec::new(),
55782                        span: None,
55783                    });
55784                }
55785            }
55786        }
55787
55788        // Parse optional alias
55789        let alias = if self.match_token(TokenType::As)
55790            || self.check(TokenType::Identifier)
55791            || self.check(TokenType::QuotedIdentifier)
55792        {
55793            if self.check(TokenType::Identifier) || self.check(TokenType::QuotedIdentifier) {
55794                let is_quoted = self.check(TokenType::QuotedIdentifier);
55795                let token = self.advance();
55796                let mut ident = Identifier::new(token.text.clone());
55797                if is_quoted {
55798                    ident.quoted = true;
55799                }
55800                Some(ident)
55801            } else {
55802                None
55803            }
55804        } else {
55805            None
55806        };
55807
55808        Ok(Some(Expression::Unnest(Box::new(UnnestFunc {
55809            this,
55810            expressions: extra_expressions,
55811            with_ordinality,
55812            alias,
55813            offset_alias,
55814        }))))
55815    }
55816
55817    /// parse_unpivot_columns - Implemented from Python _parse_unpivot_columns
55818    /// Python: parser.py:4454-4462
55819    /// Parses INTO NAME column VALUE col1, col2, ...
55820    #[allow(unused_variables, unused_mut)]
55821    pub fn parse_unpivot_columns(&mut self) -> Result<Option<Expression>> {
55822        // Must match INTO keyword
55823        if !self.match_token(TokenType::Into) {
55824            return Ok(None);
55825        }
55826
55827        // Parse NAME column
55828        let this = if self.match_text_seq(&["NAME"]) {
55829            self.parse_column()?
55830        } else {
55831            None
55832        };
55833
55834        // Parse VALUE columns
55835        let expressions = if self.match_text_seq(&["VALUE"]) {
55836            let mut cols = Vec::new();
55837            loop {
55838                if let Some(col) = self.parse_column()? {
55839                    cols.push(col);
55840                }
55841                if !self.match_token(TokenType::Comma) {
55842                    break;
55843                }
55844            }
55845            cols
55846        } else {
55847            Vec::new()
55848        };
55849
55850        // If we have either this or expressions, return an UnpivotColumns
55851        if this.is_some() || !expressions.is_empty() {
55852            Ok(Some(Expression::UnpivotColumns(Box::new(UnpivotColumns {
55853                this: Box::new(this.unwrap_or(Expression::Null(Null))),
55854                expressions,
55855            }))))
55856        } else {
55857            Ok(None)
55858        }
55859    }
55860
55861    /// parse_unquoted_field - Parses a field and converts unquoted identifiers to Var
55862    /// Python: _parse_unquoted_field
55863    pub fn parse_unquoted_field(&mut self) -> Result<Option<Expression>> {
55864        let field = self.parse_field()?;
55865
55866        // If field is an unquoted identifier, convert it to a Var
55867        match field {
55868            Some(Expression::Identifier(id)) if !id.quoted => {
55869                Ok(Some(Expression::Var(Box::new(Var { this: id.name }))))
55870            }
55871            other => Ok(other),
55872        }
55873    }
55874
55875    /// parse_user_defined_function - Parses user-defined function call
55876    /// Python: _parse_user_defined_function
55877    /// Parses: schema.function_name(param1, param2, ...)
55878    pub fn parse_user_defined_function(&mut self) -> Result<Option<Expression>> {
55879        // Parse table parts (potentially schema-qualified function name)
55880        let this = self.parse_table_parts()?;
55881        if this.is_none() {
55882            return Ok(None);
55883        }
55884
55885        // If no L_PAREN, return just the table parts (not a function call)
55886        if !self.match_token(TokenType::LParen) {
55887            return Ok(this);
55888        }
55889
55890        // Parse function parameters
55891        let mut expressions = Vec::new();
55892        if !self.check(TokenType::RParen) {
55893            loop {
55894                if let Some(param) = self.parse_function_parameter()? {
55895                    expressions.push(param);
55896                }
55897                if !self.match_token(TokenType::Comma) {
55898                    break;
55899                }
55900            }
55901        }
55902
55903        self.match_token(TokenType::RParen);
55904
55905        Ok(Some(Expression::UserDefinedFunction(Box::new(
55906            UserDefinedFunction {
55907                this: Box::new(this.unwrap()),
55908                expressions,
55909                wrapped: Some(Box::new(Expression::Boolean(BooleanLiteral {
55910                    value: true,
55911                }))),
55912            },
55913        ))))
55914    }
55915
55916    /// parse_user_defined_function_expression - Parse user-defined function expression
55917    #[allow(unused_variables, unused_mut)]
55918    pub fn parse_user_defined_function_expression(&mut self) -> Result<Option<Expression>> {
55919        // Parse a statement and wrap in Some if successful
55920        match self.parse_statement() {
55921            Ok(stmt) => Ok(Some(stmt)),
55922            Err(_) => Ok(None),
55923        }
55924    }
55925
55926    /// parse_user_defined_type - Parses a user-defined type reference
55927    /// Python: _parse_user_defined_type
55928    /// Format: schema.type_name or just type_name
55929    pub fn parse_user_defined_type(
55930        &mut self,
55931        identifier: Identifier,
55932    ) -> Result<Option<Expression>> {
55933        let mut type_name = identifier.name.clone();
55934
55935        // Handle dotted names (schema.type_name)
55936        while self.match_token(TokenType::Dot) {
55937            if !self.is_at_end() {
55938                let token = self.advance();
55939                type_name = format!("{}.{}", type_name, token.text);
55940            } else {
55941                break;
55942            }
55943        }
55944
55945        // Return as a custom data type
55946        Ok(Some(Expression::DataType(DataType::Custom {
55947            name: type_name,
55948        })))
55949    }
55950
55951    /// parse_using_identifiers - Ported from Python _parse_using_identifiers
55952    /// Parses (col1, col2, ...) for JOIN USING clause
55953    #[allow(unused_variables, unused_mut)]
55954    pub fn parse_using_identifiers(&mut self) -> Result<Option<Expression>> {
55955        // Optionally expect opening paren
55956        let has_paren = self.match_token(TokenType::LParen);
55957
55958        let mut identifiers = Vec::new();
55959        loop {
55960            // Parse column as identifier
55961            if let Some(expr) = self.parse_identifier()? {
55962                identifiers.push(expr);
55963            } else {
55964                break;
55965            }
55966            if !self.match_token(TokenType::Comma) {
55967                break;
55968            }
55969        }
55970
55971        // Match closing paren if we matched opening
55972        if has_paren {
55973            self.expect(TokenType::RParen)?;
55974        }
55975
55976        if identifiers.is_empty() {
55977            Ok(None)
55978        } else {
55979            Ok(Some(Expression::Tuple(Box::new(Tuple {
55980                expressions: identifiers,
55981            }))))
55982        }
55983    }
55984
55985    /// parse_value - Parses a value tuple for INSERT VALUES clause
55986    /// Python: _parse_value
55987    /// Syntax: (expr1, expr2, ...) or just expr (single value)
55988    pub fn parse_value(&mut self) -> Result<Option<Expression>> {
55989        // Check for parenthesized list of expressions
55990        if self.match_token(TokenType::LParen) {
55991            let mut expressions = Vec::new();
55992
55993            if !self.check(TokenType::RParen) {
55994                loop {
55995                    // Support DEFAULT keyword in VALUES
55996                    if self.match_texts(&["DEFAULT"]) {
55997                        let text = self.previous().text.to_ascii_uppercase();
55998                        expressions.push(Expression::Var(Box::new(Var { this: text })));
55999                    } else {
56000                        // Try to parse an expression
56001                        let saved_pos = self.current;
56002                        match self.parse_expression() {
56003                            Ok(expr) => expressions.push(expr),
56004                            Err(_) => {
56005                                self.current = saved_pos;
56006                            }
56007                        }
56008                    }
56009
56010                    if !self.match_token(TokenType::Comma) {
56011                        break;
56012                    }
56013                }
56014            }
56015
56016            self.match_token(TokenType::RParen);
56017            return Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))));
56018        }
56019
56020        // Single value without parentheses (some dialects support VALUES 1, 2)
56021        let saved_pos = self.current;
56022        match self.parse_expression() {
56023            Ok(expr) => {
56024                return Ok(Some(Expression::Tuple(Box::new(Tuple {
56025                    expressions: vec![expr],
56026                }))));
56027            }
56028            Err(_) => {
56029                self.current = saved_pos;
56030            }
56031        }
56032
56033        Ok(None)
56034    }
56035
56036    /// parse_var - Parse variable reference (unquoted identifier)
56037    /// Python: if self._match(TokenType.VAR): return exp.Var(this=self._prev.text)
56038    pub fn parse_var(&mut self) -> Result<Option<Expression>> {
56039        if self.match_token(TokenType::Var) {
56040            let text = self.previous().text.clone();
56041            return Ok(Some(Expression::Var(Box::new(Var { this: text }))));
56042        }
56043        // Fall back to placeholder parsing
56044        self.parse_placeholder()
56045    }
56046
56047    /// parse_var_from_options - Ported from Python _parse_var_from_options
56048    /// Parses a variable/identifier from a predefined set of options
56049    #[allow(unused_variables, unused_mut)]
56050    pub fn parse_var_from_options(&mut self) -> Result<Option<Expression>> {
56051        // Without the options dict, we just try to parse an identifier
56052        if self.is_at_end() {
56053            return Ok(None);
56054        }
56055
56056        // Get current token text as the option
56057        let token = self.peek().clone();
56058        if token.token_type == TokenType::Identifier || token.token_type == TokenType::Var {
56059            self.skip();
56060            return Ok(Some(Expression::Var(Box::new(Var {
56061                this: token.text.to_ascii_uppercase(),
56062            }))));
56063        }
56064
56065        Ok(None)
56066    }
56067
56068    /// parse_var_or_string - Delegates to parse_string
56069    #[allow(unused_variables, unused_mut)]
56070    /// parse_var_or_string - Parses a string literal or a variable
56071    /// Python: parser.py:7506-7507
56072    pub fn parse_var_or_string(&mut self) -> Result<Option<Expression>> {
56073        // Try string first, then var
56074        if let Some(s) = self.parse_string()? {
56075            return Ok(Some(s));
56076        }
56077        self.parse_var_any_token()
56078    }
56079
56080    /// parse_vector_expressions - Transforms vector type parameters
56081    /// Python: _parse_vector_expressions
56082    /// In Python, this transforms a list of expressions where the first element (identifier)
56083    /// is converted to a DataType. In Rust, since VECTOR type parsing is handled inline in
56084    /// parse_data_type, this method parses vector expressions (element_type, dimension) from
56085    /// the current position and returns them as a Tuple.
56086    pub fn parse_vector_expressions(&mut self) -> Result<Option<Expression>> {
56087        let mut expressions = Vec::new();
56088
56089        // Parse element type - convert identifier to DataType
56090        if let Some(type_expr) = self.parse_type()? {
56091            expressions.push(type_expr);
56092        } else {
56093            return Ok(None);
56094        }
56095
56096        // Parse optional dimension or additional parameters
56097        while self.match_token(TokenType::Comma) {
56098            if let Some(expr) = self.parse_primary_or_var()? {
56099                expressions.push(expr);
56100            }
56101        }
56102
56103        if expressions.is_empty() {
56104            return Ok(None);
56105        }
56106
56107        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
56108    }
56109
56110    /// parse_version - Implemented from Python _parse_version
56111    /// Python: parser.py:4266-4295
56112    /// Parses FOR SYSTEM_TIME AS OF, VERSIONS BETWEEN, etc.
56113    #[allow(unused_variables, unused_mut)]
56114    pub fn parse_version(&mut self) -> Result<Option<Expression>> {
56115        // Check for TIMESTAMP or VERSION snapshot token
56116        let this = if self.match_token(TokenType::TimestampSnapshot) {
56117            "TIMESTAMP".to_string()
56118        } else if self.match_token(TokenType::VersionSnapshot) {
56119            "VERSION".to_string()
56120        } else {
56121            return Ok(None);
56122        };
56123
56124        // Parse the kind and expression
56125        let (kind, expression) = if self.match_texts(&["FROM", "BETWEEN"]) {
56126            // FROM start TO end or BETWEEN start AND end
56127            let kind_str = self.previous().text.to_ascii_uppercase();
56128            let start = self.parse_bitwise()?;
56129            self.match_texts(&["TO", "AND"]);
56130            let end = self.parse_bitwise()?;
56131            let tuple = Expression::Tuple(Box::new(Tuple {
56132                expressions: vec![
56133                    start.unwrap_or(Expression::Null(Null)),
56134                    end.unwrap_or(Expression::Null(Null)),
56135                ],
56136            }));
56137            (kind_str, Some(Box::new(tuple)))
56138        } else if self.match_text_seq(&["CONTAINED", "IN"]) {
56139            // CONTAINED IN (values)
56140            let expressions = if self.match_token(TokenType::LParen) {
56141                let exprs = self.parse_expression_list()?;
56142                self.expect(TokenType::RParen)?;
56143                exprs
56144            } else {
56145                Vec::new()
56146            };
56147            (
56148                "CONTAINED IN".to_string(),
56149                Some(Box::new(Expression::Tuple(Box::new(Tuple { expressions })))),
56150            )
56151        } else if self.match_token(TokenType::All) {
56152            // ALL
56153            ("ALL".to_string(), None)
56154        } else {
56155            // AS OF
56156            self.match_text_seq(&["AS", "OF"]);
56157            let type_expr = self.parse_type()?;
56158            ("AS OF".to_string(), type_expr.map(Box::new))
56159        };
56160
56161        Ok(Some(Expression::Version(Box::new(Version {
56162            this: Box::new(Expression::Var(Box::new(Var { this }))),
56163            kind,
56164            expression,
56165        }))))
56166    }
56167
56168    /// parse_volatile_property - Parses VOLATILE property
56169    /// Python: _parse_volatile_property
56170    /// Returns VolatileProperty for table volatility or StabilityProperty for function stability
56171    pub fn parse_volatile_property(&mut self) -> Result<Option<Expression>> {
56172        // Check the token before VOLATILE to determine context
56173        // In SQL, VOLATILE can mean:
56174        // 1. Table volatility (CREATE VOLATILE TABLE)
56175        // 2. Function stability (CREATE FUNCTION ... VOLATILE)
56176
56177        // Look back to see if this is in a table context
56178        // PRE_VOLATILE_TOKENS typically include: CREATE, REPLACE, GLOBAL, etc.
56179        let is_table_context = if self.current >= 2 {
56180            let pre_token = &self.tokens[self.current - 2];
56181            matches!(
56182                pre_token.token_type,
56183                TokenType::Create | TokenType::Global | TokenType::Temporary | TokenType::Replace
56184            )
56185        } else {
56186            false
56187        };
56188
56189        if is_table_context {
56190            Ok(Some(Expression::VolatileProperty(Box::new(
56191                VolatileProperty { this: None },
56192            ))))
56193        } else {
56194            // Function stability - return StabilityProperty with "VOLATILE" literal
56195            Ok(Some(Expression::StabilityProperty(Box::new(
56196                StabilityProperty {
56197                    this: Box::new(Expression::Literal(Box::new(Literal::String(
56198                        "VOLATILE".to_string(),
56199                    )))),
56200                },
56201            ))))
56202        }
56203    }
56204
56205    /// parse_when_matched - Implemented from Python _parse_when_matched
56206    /// Calls: parse_disjunction, parse_star, parse_value
56207    #[allow(unused_variables, unused_mut)]
56208    /// Parse WHEN [NOT] MATCHED clauses for MERGE statements
56209    /// This is the public entry point that calls parse_when_matched_clauses
56210    pub fn parse_when_matched(&mut self) -> Result<Option<Expression>> {
56211        self.parse_when_matched_clauses()
56212    }
56213
56214    /// parse_where - Parse WHERE clause
56215    /// Python: if not self._match(TokenType.WHERE): return None; return exp.Where(this=self._parse_disjunction())
56216    pub fn parse_where(&mut self) -> Result<Option<Expression>> {
56217        if !self.match_token(TokenType::Where) {
56218            return Ok(None);
56219        }
56220        // Parse the condition expression
56221        let condition = self.parse_expression()?;
56222        Ok(Some(Expression::Where(Box::new(Where { this: condition }))))
56223    }
56224
56225    /// parse_window - Implemented from Python _parse_window
56226    /// Calls: parse_window_spec, parse_partition_and_order
56227    #[allow(unused_variables, unused_mut)]
56228    pub fn parse_window(&mut self) -> Result<Option<Expression>> {
56229        if self.match_text_seq(&["WITHIN", "GROUP"]) {
56230            return Ok(Some(Expression::WindowSpec(Box::new(WindowSpec {
56231                partition_by: Vec::new(),
56232                order_by: Vec::new(),
56233                frame: None,
56234            }))));
56235        }
56236        if self.match_text_seq(&["LAST"]) {
56237            // Matched: LAST
56238            return Ok(None);
56239        }
56240        if self.match_text_seq(&["EXCLUDE"]) {
56241            // Matched: EXCLUDE
56242            return Ok(None);
56243        }
56244        Ok(None)
56245    }
56246
56247    /// parse_window_clause - Ported from Python _parse_window_clause
56248    /// Parses WINDOW named_window_definition [, named_window_definition, ...]
56249    #[allow(unused_variables, unused_mut)]
56250    pub fn parse_window_clause(&mut self) -> Result<Option<Expression>> {
56251        if !self.match_token(TokenType::Window) {
56252            return Ok(None);
56253        }
56254
56255        // Parse comma-separated named window definitions
56256        let mut windows = Vec::new();
56257        loop {
56258            // Parse window name
56259            let name = self.parse_identifier()?;
56260            if name.is_none() {
56261                break;
56262            }
56263
56264            // Expect AS
56265            self.expect(TokenType::As)?;
56266
56267            // Parse window specification (parenthesized)
56268            self.expect(TokenType::LParen)?;
56269            let spec = self.parse_window_spec_inner()?;
56270            self.expect(TokenType::RParen)?;
56271
56272            if let (Some(name_expr), Some(spec_expr)) = (name, spec) {
56273                // Create an Alias expression wrapping the spec with the name
56274                let alias_ident = if let Expression::Identifier(id) = name_expr {
56275                    id
56276                } else {
56277                    Identifier::new("window")
56278                };
56279                windows.push(Expression::Alias(Box::new(Alias {
56280                    this: spec_expr,
56281                    alias: alias_ident,
56282                    column_aliases: Vec::new(),
56283                    pre_alias_comments: Vec::new(),
56284                    trailing_comments: Vec::new(),
56285                    inferred_type: None,
56286                })));
56287            }
56288
56289            if !self.match_token(TokenType::Comma) {
56290                break;
56291            }
56292        }
56293
56294        if windows.is_empty() {
56295            Ok(None)
56296        } else {
56297            Ok(Some(Expression::Tuple(Box::new(Tuple {
56298                expressions: windows,
56299            }))))
56300        }
56301    }
56302
56303    /// Parse window spec inner (without parentheses)
56304    fn parse_window_spec_inner(&mut self) -> Result<Option<Expression>> {
56305        // Parse optional base window name (identifier not followed by PARTITION or ORDER or DISTRIBUTE or SORT)
56306        let _base = if (self.check(TokenType::Identifier)
56307            || self.check(TokenType::QuotedIdentifier))
56308            && !self.check(TokenType::Partition)
56309            && !self.check(TokenType::Order)
56310            && !self.check(TokenType::Distribute)
56311            && !self.check(TokenType::Sort)
56312        {
56313            self.parse_identifier()?
56314        } else {
56315            None
56316        };
56317
56318        // Parse PARTITION BY or DISTRIBUTE BY (Hive uses DISTRIBUTE BY in window specs)
56319        let partition_by = if self.match_keywords(&[TokenType::Partition, TokenType::By]) {
56320            self.parse_expression_list()?
56321        } else if self.match_keywords(&[TokenType::Distribute, TokenType::By]) {
56322            // Hive: DISTRIBUTE BY is equivalent to PARTITION BY in window specs
56323            self.parse_expression_list()?
56324        } else {
56325            Vec::new()
56326        };
56327
56328        // Parse ORDER BY or SORT BY (Hive uses SORT BY in window specs)
56329        let order_by = if self.match_token(TokenType::Order) {
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 if self.match_token(TokenType::Sort) {
56344            // Hive: SORT BY is equivalent to ORDER BY in window specs
56345            self.match_token(TokenType::By);
56346            let mut orders = Vec::new();
56347            loop {
56348                if let Some(ordered) = self.parse_ordered_item()? {
56349                    orders.push(ordered);
56350                } else {
56351                    break;
56352                }
56353                if !self.match_token(TokenType::Comma) {
56354                    break;
56355                }
56356            }
56357            orders
56358        } else {
56359            Vec::new()
56360        };
56361
56362        // Parse frame specification (ROWS/RANGE/GROUPS BETWEEN ... AND ...)
56363        let frame = self.parse_window_frame()?;
56364
56365        Ok(Some(Expression::WindowSpec(Box::new(WindowSpec {
56366            partition_by,
56367            order_by,
56368            frame,
56369        }))))
56370    }
56371
56372    /// parse_window_spec - Implemented from Python _parse_window_spec
56373    #[allow(unused_variables, unused_mut)]
56374    pub fn parse_window_spec(&mut self) -> Result<Option<Expression>> {
56375        if self.match_text_seq(&["UNBOUNDED"]) {
56376            // Matched: UNBOUNDED
56377            return Ok(None);
56378        }
56379        if self.match_text_seq(&["CURRENT", "ROW"]) {
56380            // Matched: CURRENT ROW
56381            return Ok(None);
56382        }
56383        Ok(None)
56384    }
56385
56386    /// parse_with_operator - Parse column with operator class (PostgreSQL)
56387    /// Parses: ordered_expression [WITH operator]
56388    #[allow(unused_variables, unused_mut)]
56389    pub fn parse_with_operator(&mut self) -> Result<Option<Expression>> {
56390        // First parse an ordered expression with optional operator class
56391        let this = if let Some(opclass) = self.parse_opclass()? {
56392            opclass
56393        } else if let Some(ordered) = self.parse_ordered()? {
56394            ordered
56395        } else {
56396            return Ok(None);
56397        };
56398
56399        // Check for WITH operator
56400        if !self.match_token(TokenType::With) {
56401            return Ok(Some(this));
56402        }
56403
56404        // Parse the operator
56405        let op = self.parse_var()?;
56406        let op_str = match op {
56407            Some(Expression::Identifier(id)) => id.name,
56408            Some(Expression::Var(v)) => v.this.clone(),
56409            _ => String::new(),
56410        };
56411
56412        Ok(Some(Expression::WithOperator(Box::new(WithOperator {
56413            this: Box::new(this),
56414            op: op_str,
56415        }))))
56416    }
56417
56418    /// parse_with_property - Implemented from Python _parse_with_property
56419    /// Calls: parse_withjournaltable, parse_withisolatedloading, parse_wrapped_properties
56420    #[allow(unused_variables, unused_mut)]
56421    pub fn parse_with_property(&mut self) -> Result<Option<Expression>> {
56422        if self.match_text_seq(&["(", "SYSTEM_VERSIONING"]) {
56423            return Ok(Some(Expression::WithProcedureOptions(Box::new(
56424                WithProcedureOptions {
56425                    expressions: Vec::new(),
56426                },
56427            ))));
56428        }
56429        if self.match_text_seq(&["JOURNAL"]) {
56430            // Matched: JOURNAL
56431            return Ok(None);
56432        }
56433        if self.match_text_seq(&["DATA"]) {
56434            // Matched: DATA
56435            return Ok(None);
56436        }
56437        Ok(None)
56438    }
56439
56440    /// parse_withdata - Implemented from Python _parse_withdata
56441    #[allow(unused_variables, unused_mut)]
56442    pub fn parse_withdata(&mut self) -> Result<Option<Expression>> {
56443        if self.match_text_seq(&["AND", "STATISTICS"]) {
56444            return Ok(Some(Expression::WithDataProperty(Box::new(
56445                WithDataProperty {
56446                    no: None,
56447                    statistics: None,
56448                },
56449            ))));
56450        }
56451        if self.match_text_seq(&["AND", "NO", "STATISTICS"]) {
56452            // Matched: AND NO STATISTICS
56453            return Ok(None);
56454        }
56455        Ok(None)
56456    }
56457
56458    /// parse_withisolatedloading - Implemented from Python _parse_withisolatedloading
56459    #[allow(unused_variables, unused_mut)]
56460    pub fn parse_withisolatedloading(&mut self) -> Result<Option<Expression>> {
56461        if self.match_text_seq(&["NO"]) {
56462            return Ok(Some(Expression::IsolatedLoadingProperty(Box::new(
56463                IsolatedLoadingProperty {
56464                    no: None,
56465                    concurrent: None,
56466                    target: None,
56467                },
56468            ))));
56469        }
56470        if self.match_text_seq(&["CONCURRENT"]) {
56471            // Matched: CONCURRENT
56472            return Ok(None);
56473        }
56474        Ok(None)
56475    }
56476
56477    /// parse_withjournaltable - Teradata WITH JOURNAL TABLE property
56478    /// Parses: WITH JOURNAL TABLE = table_name
56479    #[allow(unused_variables, unused_mut)]
56480    pub fn parse_withjournaltable(&mut self) -> Result<Option<Expression>> {
56481        // Optionally consume TABLE keyword
56482        self.match_token(TokenType::Table);
56483
56484        // Optionally consume = sign
56485        self.match_token(TokenType::Eq);
56486
56487        // Parse the table reference
56488        let table = self.parse_table_parts()?;
56489        if table.is_none() {
56490            return Ok(None);
56491        }
56492
56493        Ok(Some(Expression::WithJournalTableProperty(Box::new(
56494            WithJournalTableProperty {
56495                this: Box::new(table.unwrap()),
56496            },
56497        ))))
56498    }
56499
56500    /// parse_wrapped - Parses an expression wrapped in parentheses
56501    /// Python: _parse_wrapped(parse_method)
56502    /// This version parses a disjunction (expression) inside parentheses
56503    pub fn parse_wrapped(&mut self) -> Result<Option<Expression>> {
56504        if !self.match_token(TokenType::LParen) {
56505            return Ok(None);
56506        }
56507
56508        let result = self.parse_disjunction()?;
56509        self.match_token(TokenType::RParen);
56510
56511        Ok(result)
56512    }
56513
56514    /// parse_wrapped_csv - Parses comma-separated expressions wrapped in parentheses
56515    /// Python: _parse_wrapped_csv(parse_method)
56516    pub fn parse_wrapped_csv(&mut self) -> Result<Option<Expression>> {
56517        if !self.match_token(TokenType::LParen) {
56518            return Ok(None);
56519        }
56520
56521        let expressions = self.parse_expression_list()?;
56522        self.match_token(TokenType::RParen);
56523
56524        if expressions.is_empty() {
56525            return Ok(None);
56526        }
56527
56528        Ok(Some(Expression::Tuple(Box::new(Tuple { expressions }))))
56529    }
56530
56531    /// parse_wrapped_id_vars - Parses comma-separated identifiers wrapped in parentheses
56532    /// Python: _parse_wrapped_id_vars
56533    pub fn parse_wrapped_id_vars(&mut self) -> Result<Option<Expression>> {
56534        if !self.match_token(TokenType::LParen) {
56535            return Ok(None);
56536        }
56537
56538        let mut identifiers = Vec::new();
56539        loop {
56540            if let Some(id) = self.parse_id_var()? {
56541                identifiers.push(id);
56542            } else {
56543                break;
56544            }
56545            if !self.match_token(TokenType::Comma) {
56546                break;
56547            }
56548        }
56549
56550        self.match_token(TokenType::RParen);
56551
56552        if identifiers.is_empty() {
56553            return Ok(None);
56554        }
56555
56556        Ok(Some(Expression::Tuple(Box::new(Tuple {
56557            expressions: identifiers,
56558        }))))
56559    }
56560
56561    /// parse_wrapped_options - Implemented from Python _parse_wrapped_options
56562    /// Parses space-separated properties wrapped in parentheses (for Snowflake STAGE_FILE_FORMAT, etc.)
56563    /// Format: = (KEY=VALUE KEY2=VALUE2 ...)
56564    pub fn parse_wrapped_options(&mut self) -> Result<Option<Expression>> {
56565        // Match optional = before opening paren
56566        self.match_token(TokenType::Eq);
56567
56568        // Expect opening paren
56569        if !self.match_token(TokenType::LParen) {
56570            return Ok(None);
56571        }
56572
56573        // Parse space-separated properties (no comma required between them)
56574        let mut properties = Vec::new();
56575        while !self.check(TokenType::RParen) && !self.is_at_end() {
56576            // Try to parse a property: KEY=VALUE
56577            if let Some(prop) = self.parse_option_property()? {
56578                properties.push(prop);
56579            } else {
56580                break;
56581            }
56582        }
56583
56584        // Expect closing paren
56585        self.match_token(TokenType::RParen);
56586
56587        if properties.is_empty() {
56588            Ok(None)
56589        } else {
56590            Ok(Some(Expression::Tuple(Box::new(Tuple {
56591                expressions: properties,
56592            }))))
56593        }
56594    }
56595
56596    /// Parse a single option property: KEY=VALUE
56597    /// Handles various value types: identifiers, strings, numbers, nested parens like ('') or (val1, val2)
56598    fn parse_option_property(&mut self) -> Result<Option<Expression>> {
56599        // Save position to retreat if this isn't a property
56600        let index = self.current;
56601
56602        // Parse the key (identifier/column name)
56603        // For Snowflake options, keys are identifiers like TYPE, FIELD_DELIMITER, NULL_IF, etc.
56604        let key = if self.check(TokenType::Identifier)
56605            || self.check(TokenType::Var)
56606            || self
56607                .peek()
56608                .text
56609                .chars()
56610                .all(|c| c.is_ascii_alphanumeric() || c == '_')
56611        {
56612            let name = self.peek().text.clone();
56613            self.skip();
56614            Some(Expression::Var(Box::new(Var { this: name })))
56615        } else {
56616            None
56617        };
56618
56619        let key = match key {
56620            Some(k) => k,
56621            None => {
56622                self.current = index;
56623                return Ok(None);
56624            }
56625        };
56626
56627        // Expect =
56628        if !self.match_token(TokenType::Eq) {
56629            self.current = index;
56630            return Ok(None);
56631        }
56632
56633        // Parse the value - can be:
56634        // - Simple identifier: CSV, SKIP_FILE, BASE64, TRUE, FALSE, CASE_SENSITIVE
56635        // - String literal: '|', '"', 'TZHTZM YYYY-MM-DD HH24:MI:SS.FF9'
56636        // - Number: 5
56637        // - Nested parens for tuple: ('')
56638        let value = if self.check(TokenType::LParen) {
56639            // Parse nested parenthesized value like NULL_IF=('')
56640            self.skip(); // consume (
56641            let mut inner_exprs = Vec::new();
56642            while !self.check(TokenType::RParen) && !self.is_at_end() {
56643                if let Some(expr) = self.parse_primary_for_option()? {
56644                    inner_exprs.push(expr);
56645                }
56646                // Allow comma between nested values
56647                self.match_token(TokenType::Comma);
56648            }
56649            self.match_token(TokenType::RParen);
56650            Expression::Tuple(Box::new(Tuple {
56651                expressions: inner_exprs,
56652            }))
56653        } else if let Some(primary) = self.parse_primary_for_option()? {
56654            primary
56655        } else {
56656            // Fallback: try to parse as a var
56657            let text = self.peek().text.clone();
56658            self.skip();
56659            Expression::Var(Box::new(Var { this: text }))
56660        };
56661
56662        // Return as a Property expression (KEY=VALUE)
56663        Ok(Some(Expression::Property(Box::new(Property {
56664            this: Box::new(key),
56665            value: Some(Box::new(value)),
56666        }))))
56667    }
56668
56669    /// Parse a primary value for option properties
56670    /// Handles strings, numbers, identifiers, TRUE/FALSE
56671    fn parse_primary_for_option(&mut self) -> Result<Option<Expression>> {
56672        // String literal
56673        if self.check(TokenType::String) {
56674            let text = self.peek().text.clone();
56675            self.skip();
56676            return Ok(Some(Expression::Literal(Box::new(Literal::String(text)))));
56677        }
56678
56679        // Number
56680        if self.check(TokenType::Number) {
56681            let text = self.peek().text.clone();
56682            self.skip();
56683            return Ok(Some(Expression::Literal(Box::new(Literal::Number(text)))));
56684        }
56685
56686        // TRUE/FALSE
56687        if self.check(TokenType::True) {
56688            self.skip();
56689            return Ok(Some(Expression::Boolean(BooleanLiteral { value: true })));
56690        }
56691        if self.check(TokenType::False) {
56692            self.skip();
56693            return Ok(Some(Expression::Boolean(BooleanLiteral { value: false })));
56694        }
56695
56696        // Identifier or keyword used as value (CSV, SKIP_FILE, BASE64, etc.)
56697        if self.check(TokenType::Identifier)
56698            || self.check(TokenType::Var)
56699            || (!self.check(TokenType::RParen)
56700                && !self.check(TokenType::Comma)
56701                && !self.check(TokenType::Eq)
56702                && !self.is_at_end())
56703        {
56704            let text = self.peek().text.clone();
56705            // Don't consume if it's a closing paren or could be the next property key followed by =
56706            if self.check(TokenType::RParen) {
56707                return Ok(None);
56708            }
56709            // Check if this is the start of next property (followed by =)
56710            if self.check_next(TokenType::Eq) {
56711                return Ok(None);
56712            }
56713            self.skip();
56714            return Ok(Some(Expression::Var(Box::new(Var { this: text }))));
56715        }
56716
56717        Ok(None)
56718    }
56719
56720    /// parse_options_list - Parses BigQuery-style OPTIONS list: (key=value, key=value, ...)
56721    /// Parses key=value assignments where values can be complex expressions
56722    pub fn parse_options_list(&mut self) -> Result<Vec<Expression>> {
56723        // Expect opening paren
56724        if !self.match_token(TokenType::LParen) {
56725            return Ok(Vec::new());
56726        }
56727
56728        // Parse comma-separated key=value pairs
56729        let mut options = Vec::new();
56730        loop {
56731            // Check for empty OPTIONS () or end of list
56732            if self.check(TokenType::RParen) {
56733                break;
56734            }
56735
56736            // Parse key=value using parse_assignment which handles EQ operations
56737            if let Some(opt) = self.parse_assignment()? {
56738                options.push(opt);
56739            } else {
56740                break;
56741            }
56742
56743            if !self.match_token(TokenType::Comma) {
56744                break;
56745            }
56746        }
56747
56748        // Expect closing paren
56749        self.expect(TokenType::RParen)?;
56750
56751        Ok(options)
56752    }
56753
56754    /// Parse BigQuery PARTITION BY property and return a typed AST node.
56755    fn parse_bigquery_partition_by_property(&mut self) -> Result<Option<Expression>> {
56756        let start = self.current;
56757        let matched_partition = if self.match_token(TokenType::PartitionBy) {
56758            true
56759        } else if self.match_token(TokenType::Partition) {
56760            self.match_token(TokenType::By)
56761        } else {
56762            false
56763        };
56764
56765        if !matched_partition {
56766            self.current = start;
56767            return Ok(None);
56768        }
56769
56770        let mut expressions = Vec::new();
56771        while !self.is_at_end()
56772            && !self.check(TokenType::Cluster)
56773            && !self.check(TokenType::As)
56774            && !self.check(TokenType::Semicolon)
56775            && !self.check(TokenType::RParen)
56776            && !self.check_identifier("OPTIONS")
56777        {
56778            match self.parse_expression() {
56779                Ok(expr) => expressions.push(expr),
56780                Err(_) => {
56781                    // Fall back to generic/raw parsing if typed parsing can't consume this form.
56782                    self.current = start;
56783                    return Ok(None);
56784                }
56785            }
56786
56787            if !self.match_token(TokenType::Comma) {
56788                break;
56789            }
56790        }
56791
56792        if expressions.is_empty() {
56793            self.current = start;
56794            return Ok(None);
56795        }
56796
56797        Ok(Some(Expression::PartitionByProperty(Box::new(
56798            PartitionByProperty { expressions },
56799        ))))
56800    }
56801
56802    /// Parse BigQuery CLUSTER BY property and return a typed AST node.
56803    fn parse_bigquery_cluster_by_property(&mut self) -> Result<Option<Expression>> {
56804        let start = self.current;
56805        if !self.match_keywords(&[TokenType::Cluster, TokenType::By]) {
56806            self.current = start;
56807            return Ok(None);
56808        }
56809
56810        let mut columns = Vec::new();
56811        loop {
56812            if let Some(Expression::Identifier(id)) = self.parse_identifier()? {
56813                columns.push(id);
56814            } else if self.is_identifier_or_keyword_token() {
56815                let name = self.advance().text;
56816                columns.push(Identifier {
56817                    name,
56818                    quoted: false,
56819                    trailing_comments: Vec::new(),
56820                    span: None,
56821                });
56822            } else {
56823                // Fall back to generic/raw parsing if typed parsing can't consume this form.
56824                self.current = start;
56825                return Ok(None);
56826            }
56827
56828            if !self.match_token(TokenType::Comma) {
56829                break;
56830            }
56831        }
56832
56833        if columns.is_empty() {
56834            self.current = start;
56835            return Ok(None);
56836        }
56837
56838        Ok(Some(Expression::ClusterByColumnsProperty(Box::new(
56839            ClusterByColumnsProperty { columns },
56840        ))))
56841    }
56842
56843    /// Parse BigQuery OPTIONS (...) clause into typed entries when possible.
56844    /// Falls back to generic `Properties` when options are not simple key/value assignments.
56845    fn parse_bigquery_options_property(&mut self) -> Result<Option<Expression>> {
56846        let start = self.current;
56847        if !self.match_identifier("OPTIONS") {
56848            self.current = start;
56849            return Ok(None);
56850        }
56851
56852        let options = self.parse_options_list()?;
56853        if options.is_empty() {
56854            return Ok(Some(Expression::OptionsProperty(Box::new(
56855                OptionsProperty {
56856                    entries: Vec::new(),
56857                },
56858            ))));
56859        }
56860
56861        let mut entries = Vec::new();
56862        for option_expr in &options {
56863            let Some(entry) = Self::option_entry_from_expression(option_expr) else {
56864                return Ok(Some(Expression::Properties(Box::new(Properties {
56865                    expressions: options,
56866                }))));
56867            };
56868            entries.push(entry);
56869        }
56870
56871        Ok(Some(Expression::OptionsProperty(Box::new(
56872            OptionsProperty { entries },
56873        ))))
56874    }
56875
56876    fn option_entry_from_expression(expr: &Expression) -> Option<OptionEntry> {
56877        let Expression::Eq(eq) = expr else {
56878            return None;
56879        };
56880
56881        let key = match &eq.left {
56882            Expression::Column(col) if col.table.is_none() => col.name.clone(),
56883            Expression::Identifier(id) => id.clone(),
56884            Expression::Var(var) => Identifier {
56885                name: var.this.clone(),
56886                quoted: false,
56887                trailing_comments: Vec::new(),
56888                span: None,
56889            },
56890            _ => return None,
56891        };
56892
56893        Some(OptionEntry {
56894            key,
56895            value: eq.right.clone(),
56896        })
56897    }
56898
56899    /// parse_environment_list - Parses Databricks ENVIRONMENT list: (dependencies = '...', environment_version = '...')
56900    /// Parses key=value assignments where values can be string literals
56901    pub fn parse_environment_list(&mut self) -> Result<Vec<Expression>> {
56902        // Expect opening paren
56903        if !self.match_token(TokenType::LParen) {
56904            return Ok(Vec::new());
56905        }
56906
56907        // Parse comma-separated key=value pairs
56908        let mut env_items = Vec::new();
56909        loop {
56910            // Check for empty ENVIRONMENT () or end of list
56911            if self.check(TokenType::RParen) {
56912                break;
56913            }
56914
56915            // Parse key=value using parse_assignment which handles EQ operations
56916            if let Some(opt) = self.parse_assignment()? {
56917                env_items.push(opt);
56918            } else {
56919                break;
56920            }
56921
56922            if !self.match_token(TokenType::Comma) {
56923                break;
56924            }
56925        }
56926
56927        // Expect closing paren
56928        self.expect(TokenType::RParen)?;
56929
56930        Ok(env_items)
56931    }
56932
56933    /// parse_wrapped_properties - Ported from Python _parse_wrapped_properties
56934    /// Parses properties wrapped in parentheses
56935    #[allow(unused_variables, unused_mut)]
56936    pub fn parse_wrapped_properties(&mut self) -> Result<Option<Expression>> {
56937        // Parse wrapped list of properties: (prop1, prop2, ...)
56938        if !self.match_token(TokenType::LParen) {
56939            return Ok(None);
56940        }
56941
56942        let mut props = Vec::new();
56943        loop {
56944            if let Some(prop) = self.parse_property()? {
56945                props.push(prop);
56946            }
56947            if !self.match_token(TokenType::Comma) {
56948                break;
56949            }
56950        }
56951
56952        self.match_token(TokenType::RParen);
56953
56954        if props.is_empty() {
56955            return Ok(None);
56956        }
56957
56958        // Return as a Properties expression
56959        Ok(Some(Expression::Properties(Box::new(Properties {
56960            expressions: props,
56961        }))))
56962    }
56963
56964    /// parse_wrapped_select - Ported from Python _parse_wrapped_select
56965    /// Parses wrapped select statements including PIVOT/UNPIVOT and FROM-first syntax
56966    #[allow(unused_variables, unused_mut)]
56967    pub fn parse_wrapped_select(&mut self, table: bool) -> Result<Option<Expression>> {
56968        // Check for PIVOT/UNPIVOT
56969        let is_unpivot = self.check(TokenType::Unpivot);
56970        if self.match_token(TokenType::Pivot) || self.match_token(TokenType::Unpivot) {
56971            // Call simplified pivot parser
56972            return self.parse_simplified_pivot(is_unpivot);
56973        }
56974
56975        // Check for FROM (DuckDB FROM-first syntax)
56976        if self.match_token(TokenType::From) {
56977            // Parse the FROM clause (table reference)
56978            let from_expr = self.parse_table()?;
56979
56980            // Try to parse a full SELECT
56981            let select = self.parse_select_query()?;
56982
56983            if let Some(sel) = select {
56984                // Apply set operations and query modifiers
56985                let with_ops = self.parse_set_operations_with_expr(Some(sel))?;
56986                return Ok(with_ops);
56987            } else if let Some(from_table) = from_expr {
56988                // Create a SELECT * FROM <table>
56989                let mut select_struct = Select::new();
56990                select_struct.expressions = vec![Expression::Star(Star {
56991                    table: None,
56992                    except: None,
56993                    replace: None,
56994                    rename: None,
56995                    trailing_comments: Vec::new(),
56996                    span: None,
56997                })];
56998                select_struct.from = Some(From {
56999                    expressions: vec![from_table],
57000                });
57001                let select_all = Expression::Select(Box::new(select_struct));
57002                let with_ops = self.parse_set_operations_with_expr(Some(select_all))?;
57003                return Ok(with_ops);
57004            }
57005            return Ok(None);
57006        }
57007
57008        // Regular case: parse table or nested select
57009        let this = if table {
57010            self.parse_table()?
57011        } else {
57012            // Parse nested select without set operations
57013            self.parse_select_query()?
57014        };
57015
57016        if this.is_none() {
57017            return Ok(None);
57018        }
57019
57020        // Apply set operations and query modifiers
57021        let with_ops = self.parse_set_operations_with_expr(this)?;
57022        Ok(with_ops)
57023    }
57024
57025    /// Helper for parse_wrapped_select with default table=false
57026    pub fn parse_wrapped_select_default(&mut self) -> Result<Option<Expression>> {
57027        self.parse_wrapped_select(false)
57028    }
57029
57030    /// parse_xml_element - Implemented from Python _parse_xml_element
57031    /// Python: parser.py:6917-6931
57032    /// Parses XMLELEMENT(NAME name [, expr, ...]) or XMLELEMENT(EVALNAME expr [, expr, ...])
57033    #[allow(unused_variables, unused_mut)]
57034    pub fn parse_xml_element(&mut self) -> Result<Option<Expression>> {
57035        let (this, evalname) = if self.match_text_seq(&["EVALNAME"]) {
57036            // EVALNAME - parse expression for dynamic element name
57037            let expr = self.parse_bitwise()?;
57038            (
57039                expr,
57040                Some(Box::new(Expression::Boolean(BooleanLiteral {
57041                    value: true,
57042                }))),
57043            )
57044        } else {
57045            // NAME - parse static element name
57046            self.match_text_seq(&["NAME"]);
57047            let id = self.parse_id_var()?;
57048            (id, None)
57049        };
57050
57051        // Parse optional expressions (comma-separated content/attributes)
57052        let expressions = if self.match_token(TokenType::Comma) {
57053            self.parse_expression_list()?
57054        } else {
57055            Vec::new()
57056        };
57057
57058        match this {
57059            Some(t) => Ok(Some(Expression::XMLElement(Box::new(XMLElement {
57060                this: Box::new(t),
57061                expressions,
57062                evalname,
57063            })))),
57064            None => Ok(None),
57065        }
57066    }
57067
57068    /// parse_xml_namespace - Ported from Python _parse_xml_namespace
57069    /// Parses XML namespace declarations
57070    #[allow(unused_variables, unused_mut)]
57071    pub fn parse_xml_namespace(&mut self) -> Result<Option<Expression>> {
57072        let mut namespaces = Vec::new();
57073
57074        loop {
57075            // Check for DEFAULT namespace
57076            let is_default = self.match_text_seq(&["DEFAULT"]);
57077
57078            // Parse the URI string
57079            let uri = if is_default {
57080                self.parse_string()?
57081            } else {
57082                // Parse URI with optional alias (AS name)
57083                let uri_expr = self.parse_string()?;
57084                if let Some(u) = uri_expr {
57085                    self.parse_alias_with_expr(Some(u))?
57086                } else {
57087                    None
57088                }
57089            };
57090
57091            if let Some(u) = uri {
57092                namespaces.push(u);
57093            }
57094
57095            // Continue if comma
57096            if !self.match_token(TokenType::Comma) {
57097                break;
57098            }
57099        }
57100
57101        if namespaces.is_empty() {
57102            return Ok(None);
57103        }
57104
57105        // Return as a Tuple (list of namespaces)
57106        Ok(Some(Expression::Tuple(Box::new(Tuple {
57107            expressions: namespaces,
57108        }))))
57109    }
57110
57111    /// parse_xml_table - Implemented from Python _parse_xml_table
57112    /// Python: parser.py:6933-6961
57113    /// Parses XMLTABLE(xpath_expr PASSING xml_doc COLUMNS ...)
57114    #[allow(unused_variables, unused_mut)]
57115    pub fn parse_xml_table(&mut self) -> Result<Option<Expression>> {
57116        // Parse optional XMLNAMESPACES clause
57117        let namespaces = if self.match_text_seq(&["XMLNAMESPACES", "("]) {
57118            let ns = self.parse_xml_namespace()?;
57119            self.match_text_seq(&[")", ","]);
57120            ns.map(Box::new)
57121        } else {
57122            None
57123        };
57124
57125        // Parse XPath expression (string)
57126        let this = self.parse_string()?;
57127        if this.is_none() {
57128            return Ok(None);
57129        }
57130
57131        // Parse PASSING clause
57132        let passing = if self.match_text_seq(&["PASSING"]) {
57133            // BY VALUE is optional
57134            self.match_text_seq(&["BY", "VALUE"]);
57135            // Parse comma-separated expressions.
57136            // Oracle XMLTABLE PASSING accepts full expressions (including function calls),
57137            // not just column references.
57138            // We need to stop before COLUMNS, RETURNING, or )
57139            let mut cols = Vec::new();
57140            loop {
57141                // Check for stop keywords before parsing a column
57142                if !self.is_at_end() {
57143                    let next_text = self.peek().text.to_ascii_uppercase();
57144                    if next_text == "COLUMNS" || next_text == "RETURNING" {
57145                        break;
57146                    }
57147                    if self.check(TokenType::RParen) {
57148                        break;
57149                    }
57150                }
57151                if let Some(col) = self.parse_assignment()? {
57152                    cols.push(col);
57153                } else {
57154                    break;
57155                }
57156                if !self.match_token(TokenType::Comma) {
57157                    break;
57158                }
57159            }
57160            if cols.is_empty() {
57161                None
57162            } else {
57163                Some(Box::new(Expression::Tuple(Box::new(Tuple {
57164                    expressions: cols,
57165                }))))
57166            }
57167        } else {
57168            None
57169        };
57170
57171        // Parse optional RETURNING SEQUENCE BY REF
57172        let by_ref = if self.match_text_seq(&["RETURNING", "SEQUENCE", "BY", "REF"]) {
57173            Some(Box::new(Expression::Boolean(BooleanLiteral {
57174                value: true,
57175            })))
57176        } else {
57177            None
57178        };
57179
57180        // Parse COLUMNS clause
57181        let columns = if self.match_text_seq(&["COLUMNS"]) {
57182            let mut cols = Vec::new();
57183            loop {
57184                // Stop if we hit the closing paren
57185                if self.check(TokenType::RParen) {
57186                    break;
57187                }
57188                // Be permissive with leading commas in multiline XMLTABLE COLUMNS lists.
57189                if self.match_token(TokenType::Comma) {
57190                    continue;
57191                }
57192                if let Some(col_def) = self.parse_field_def()? {
57193                    cols.push(col_def);
57194                } else {
57195                    break;
57196                }
57197                if !self.match_token(TokenType::Comma) {
57198                    break;
57199                }
57200            }
57201            cols
57202        } else {
57203            Vec::new()
57204        };
57205
57206        Ok(Some(Expression::XMLTable(Box::new(XMLTable {
57207            this: Box::new(this.unwrap()),
57208            namespaces,
57209            passing,
57210            columns,
57211            by_ref,
57212        }))))
57213    }
57214
57215    /// Parse UNLOAD statement (Athena/Presto/Redshift)
57216    /// UNLOAD (SELECT ...) TO 'location' WITH (options)
57217    fn parse_unload(&mut self) -> Result<Expression> {
57218        // Collect entire statement as a Command
57219        let mut parts = Vec::new();
57220        parts.push(self.advance().text.clone()); // consume UNLOAD
57221        parts.push(" ".to_string()); // space after UNLOAD
57222
57223        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
57224            let token_type = self.peek().token_type;
57225            let token_text = self.peek().text.clone();
57226
57227            // Track string literals
57228            if token_type == TokenType::String {
57229                parts.push(format!("'{}'", token_text.replace('\'', "''")));
57230                self.skip();
57231                // Add space after string unless followed by punctuation
57232                if !self.is_at_end() {
57233                    let next_type = self.peek().token_type;
57234                    if !matches!(
57235                        next_type,
57236                        TokenType::Comma | TokenType::RParen | TokenType::Semicolon
57237                    ) {
57238                        parts.push(" ".to_string());
57239                    }
57240                }
57241                continue;
57242            }
57243
57244            // Handle ARRAY[...] syntax - no space between ARRAY and [
57245            if token_text.eq_ignore_ascii_case("ARRAY")
57246                && self
57247                    .peek_nth(1)
57248                    .is_some_and(|t| t.token_type == TokenType::LBracket)
57249            {
57250                parts.push(token_text);
57251                self.skip();
57252                // Consume [
57253                parts.push("[".to_string());
57254                self.skip();
57255                // Collect until RBracket
57256                while !self.is_at_end() && !self.check(TokenType::RBracket) {
57257                    let inner_type = self.peek().token_type;
57258                    let inner_text = self.peek().text.clone();
57259                    if inner_type == TokenType::String {
57260                        parts.push(format!("'{}'", inner_text.replace('\'', "''")));
57261                    } else {
57262                        parts.push(inner_text);
57263                    }
57264                    self.skip();
57265                    if self.check(TokenType::Comma) {
57266                        parts.push(", ".to_string());
57267                        self.skip();
57268                    }
57269                }
57270                if self.check(TokenType::RBracket) {
57271                    parts.push("]".to_string());
57272                    self.skip();
57273                }
57274                continue;
57275            }
57276
57277            parts.push(token_text);
57278            self.skip();
57279
57280            // Add space after most tokens except punctuation
57281            if !self.is_at_end() {
57282                let next_type = self.peek().token_type;
57283                let no_space_before = matches!(
57284                    next_type,
57285                    TokenType::Comma
57286                        | TokenType::RParen
57287                        | TokenType::RBracket
57288                        | TokenType::Semicolon
57289                        | TokenType::LBracket
57290                );
57291                let no_space_after = matches!(token_type, TokenType::LParen | TokenType::LBracket);
57292                if !no_space_before && !no_space_after {
57293                    parts.push(" ".to_string());
57294                }
57295            }
57296        }
57297
57298        Ok(Expression::Command(Box::new(Command {
57299            this: parts.join(""),
57300        })))
57301    }
57302
57303    /// Parse USING EXTERNAL FUNCTION statement (Athena)
57304    /// USING EXTERNAL FUNCTION name(params) RETURNS type LAMBDA 'arn' SELECT ...
57305    fn parse_using_external_function(&mut self) -> Result<Expression> {
57306        // Record start position
57307        let start_pos = self.peek().span.start;
57308
57309        // Advance through all tokens until end or semicolon
57310        while !self.is_at_end() && !self.check(TokenType::Semicolon) {
57311            self.skip();
57312        }
57313
57314        // Get end position from the last consumed token
57315        let end_pos = if self.current > 0 {
57316            self.tokens[self.current - 1].span.end
57317        } else {
57318            start_pos
57319        };
57320
57321        // Extract exact text from source if available
57322        let command_text = if let Some(ref source) = self.source {
57323            source[start_pos..end_pos].to_string()
57324        } else {
57325            // Fallback: reconstruct from tokens (loses whitespace)
57326            let mut parts = Vec::new();
57327            for i in 0..self.current {
57328                if self.tokens[i].span.start >= start_pos && self.tokens[i].span.end <= end_pos {
57329                    if self.tokens[i].token_type == TokenType::String {
57330                        parts.push(format!("'{}'", self.tokens[i].text.replace('\'', "''")));
57331                    } else {
57332                        parts.push(self.tokens[i].text.clone());
57333                    }
57334                    if i + 1 < self.current {
57335                        parts.push(" ".to_string());
57336                    }
57337                }
57338            }
57339            parts.join("")
57340        };
57341
57342        Ok(Expression::Command(Box::new(Command {
57343            this: command_text,
57344        })))
57345    }
57346}
57347
57348#[cfg(test)]
57349mod tests {
57350    use super::*;
57351    use crate::traversal::ExpressionWalk;
57352
57353    #[test]
57354    fn test_comment_before_limit() {
57355        let sql = "SELECT a FROM b WHERE foo AND bla\n-- comment 3\nLIMIT 10";
57356        let result = Parser::parse_sql(sql).unwrap();
57357        let output = crate::Generator::sql(&result[0]).unwrap();
57358        assert_eq!(
57359            output,
57360            "SELECT a FROM b WHERE foo AND bla LIMIT 10 /* comment 3 */"
57361        );
57362    }
57363
57364    #[test]
57365    fn test_variadic_array_postgres() {
57366        use crate::dialects::DialectType;
57367        use crate::transpile;
57368
57369        // Test: ARRAY[10, -1, 5, 4.4] should parse correctly in Postgres
57370        let sql = "SELECT ARRAY[10, -1, 5, 4.4]";
57371        let result = transpile(sql, DialectType::PostgreSQL, DialectType::PostgreSQL).unwrap();
57372        eprintln!("Array test: {} -> {}", sql, result[0]);
57373
57374        // Test: VARIADIC ARRAY[10, -1, 5, 4.4] in function call
57375        let sql2 = "SELECT MLEAST(VARIADIC ARRAY[10, -1, 5, 4.4])";
57376        let result2 = transpile(sql2, DialectType::PostgreSQL, DialectType::PostgreSQL).unwrap();
57377        eprintln!("VARIADIC test: {} -> {}", sql2, result2[0]);
57378        assert_eq!(result2[0], sql2);
57379    }
57380
57381    #[test]
57382    fn test_parse_simple_select() {
57383        let result = Parser::parse_sql("SELECT 1").unwrap();
57384        assert_eq!(result.len(), 1);
57385        assert!(result[0].is_select());
57386    }
57387
57388    #[test]
57389    fn test_parse_select_from() {
57390        let result = Parser::parse_sql("SELECT a, b FROM t").unwrap();
57391        assert_eq!(result.len(), 1);
57392
57393        let select = result[0].as_select().unwrap();
57394        assert_eq!(select.expressions.len(), 2);
57395        assert!(select.from.is_some());
57396    }
57397
57398    #[test]
57399    fn test_parse_select_where() {
57400        let result = Parser::parse_sql("SELECT * FROM t WHERE x = 1").unwrap();
57401        let select = result[0].as_select().unwrap();
57402        assert!(select.where_clause.is_some());
57403    }
57404
57405    #[test]
57406    fn test_parse_balances_large_and_chain_depth() {
57407        let mut sql = String::from("SELECT 1 WHERE c0 = 0");
57408        for i in 1..4096 {
57409            sql.push_str(&format!(" AND c{i} = {i}"));
57410        }
57411
57412        let result = Parser::parse_sql(&sql).unwrap();
57413        let select = result[0].as_select().unwrap();
57414        let where_clause = select.where_clause.as_ref().expect("WHERE clause missing");
57415        let depth = where_clause.this.tree_depth();
57416        assert!(
57417            depth < 128,
57418            "Expected balanced boolean tree depth, got {}",
57419            depth
57420        );
57421    }
57422
57423    #[test]
57424    fn test_parse_balances_large_or_chain_depth() {
57425        let mut sql = String::from("SELECT 1 WHERE c0 = 0");
57426        for i in 1..4096 {
57427            sql.push_str(&format!(" OR c{i} = {i}"));
57428        }
57429
57430        let result = Parser::parse_sql(&sql).unwrap();
57431        let select = result[0].as_select().unwrap();
57432        let where_clause = select.where_clause.as_ref().expect("WHERE clause missing");
57433        let depth = where_clause.this.tree_depth();
57434        assert!(
57435            depth < 128,
57436            "Expected balanced boolean tree depth, got {}",
57437            depth
57438        );
57439    }
57440
57441    #[test]
57442    fn test_parse_select_join() {
57443        let result = Parser::parse_sql("SELECT * FROM a JOIN b ON a.id = b.id").unwrap();
57444        let select = result[0].as_select().unwrap();
57445        assert_eq!(select.joins.len(), 1);
57446        assert_eq!(select.joins[0].kind, JoinKind::Inner);
57447    }
57448
57449    #[test]
57450    fn test_parse_expression_precedence() {
57451        let result = Parser::parse_sql("SELECT 1 + 2 * 3").unwrap();
57452        let select = result[0].as_select().unwrap();
57453        // Should parse as 1 + (2 * 3) due to precedence
57454        assert!(matches!(select.expressions[0], Expression::Add(_)));
57455    }
57456
57457    #[test]
57458    fn test_parse_function() {
57459        // COUNT(*) is now a typed Count expression
57460        let result = Parser::parse_sql("SELECT COUNT(*)").unwrap();
57461        let select = result[0].as_select().unwrap();
57462        assert!(matches!(select.expressions[0], Expression::Count(_)));
57463
57464        // Unknown functions stay as generic Function
57465        let result = Parser::parse_sql("SELECT MY_CUSTOM_FUNC(name)").unwrap();
57466        let select = result[0].as_select().unwrap();
57467        assert!(matches!(select.expressions[0], Expression::Function(_)));
57468
57469        // Known aggregate functions are now typed
57470        let result = Parser::parse_sql("SELECT SUM(amount)").unwrap();
57471        let select = result[0].as_select().unwrap();
57472        assert!(matches!(select.expressions[0], Expression::Sum(_)));
57473    }
57474
57475    #[test]
57476    fn test_parse_window_function() {
57477        let result =
57478            Parser::parse_sql("SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)")
57479                .unwrap();
57480        let select = result[0].as_select().unwrap();
57481        assert!(matches!(
57482            select.expressions[0],
57483            Expression::WindowFunction(_)
57484        ));
57485    }
57486
57487    #[test]
57488    fn test_parse_window_function_with_frame() {
57489        let result = Parser::parse_sql("SELECT SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)").unwrap();
57490        let select = result[0].as_select().unwrap();
57491        assert!(matches!(
57492            select.expressions[0],
57493            Expression::WindowFunction(_)
57494        ));
57495    }
57496
57497    #[test]
57498    fn test_parse_subscript() {
57499        // Array subscript
57500        let result = Parser::parse_sql("SELECT arr[0]").unwrap();
57501        let select = result[0].as_select().unwrap();
57502        assert!(matches!(select.expressions[0], Expression::Subscript(_)));
57503
57504        // Function result subscript
57505        let result = Parser::parse_sql("SELECT SPLIT(name, ',')[0]").unwrap();
57506        let select = result[0].as_select().unwrap();
57507        assert!(matches!(select.expressions[0], Expression::Subscript(_)));
57508    }
57509
57510    #[test]
57511    fn test_parse_case() {
57512        let result = Parser::parse_sql("SELECT CASE WHEN x = 1 THEN 'a' ELSE 'b' END").unwrap();
57513        let select = result[0].as_select().unwrap();
57514        assert!(matches!(select.expressions[0], Expression::Case(_)));
57515    }
57516
57517    #[test]
57518    fn test_parse_insert() {
57519        let result = Parser::parse_sql("INSERT INTO t (a, b) VALUES (1, 2)").unwrap();
57520        assert!(matches!(result[0], Expression::Insert(_)));
57521    }
57522
57523    #[test]
57524    fn test_parse_template_variable() {
57525        // Test Databricks/Hive ${variable} syntax
57526        let result = Parser::parse_sql("SELECT ${x} FROM ${y} WHERE ${z} > 1").unwrap();
57527        let select = result[0].as_select().unwrap();
57528        // The expression should be a Parameter with DollarBrace style
57529        assert!(
57530            matches!(&select.expressions[0], Expression::Parameter(p) if p.name == Some("x".to_string()))
57531        );
57532        // Check the style is DollarBrace
57533        if let Expression::Parameter(p) = &select.expressions[0] {
57534            assert_eq!(p.style, ParameterStyle::DollarBrace);
57535        }
57536    }
57537
57538    #[test]
57539    fn test_parse_update() {
57540        let result = Parser::parse_sql("UPDATE t SET a = 1 WHERE b = 2").unwrap();
57541        assert!(matches!(result[0], Expression::Update(_)));
57542    }
57543
57544    #[test]
57545    fn test_parse_delete() {
57546        let result = Parser::parse_sql("DELETE FROM t WHERE a = 1").unwrap();
57547        assert!(matches!(result[0], Expression::Delete(_)));
57548    }
57549
57550    // DDL tests
57551    #[test]
57552    fn test_parse_create_table() {
57553        let result = Parser::parse_sql(
57554            "CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100) NOT NULL)",
57555        )
57556        .unwrap();
57557        assert!(matches!(result[0], Expression::CreateTable(_)));
57558
57559        if let Expression::CreateTable(ct) = &result[0] {
57560            assert_eq!(ct.name.name.name, "users");
57561            assert_eq!(ct.columns.len(), 2);
57562            assert!(ct.columns[0].primary_key);
57563            assert_eq!(ct.columns[1].nullable, Some(false));
57564        }
57565    }
57566
57567    #[test]
57568    fn test_parse_create_table_if_not_exists() {
57569        let result = Parser::parse_sql("CREATE TABLE IF NOT EXISTS t (id INT)").unwrap();
57570        if let Expression::CreateTable(ct) = &result[0] {
57571            assert!(ct.if_not_exists);
57572        }
57573    }
57574
57575    #[test]
57576    fn test_parse_create_temporary_table() {
57577        let result = Parser::parse_sql("CREATE TEMPORARY TABLE t (id INT)").unwrap();
57578        if let Expression::CreateTable(ct) = &result[0] {
57579            assert!(ct.temporary);
57580        }
57581    }
57582
57583    #[test]
57584    fn test_bigquery_create_table_properties_are_typed() {
57585        use crate::DialectType;
57586
57587        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";
57588        let parsed = crate::parse(sql, DialectType::BigQuery).unwrap();
57589
57590        let create = match &parsed[0] {
57591            Expression::CreateTable(ct) => ct,
57592            other => panic!(
57593                "Expected CreateTable, got {:?}",
57594                std::mem::discriminant(other)
57595            ),
57596        };
57597
57598        assert!(
57599            create
57600                .properties
57601                .iter()
57602                .any(|p| matches!(p, Expression::PartitionByProperty(_))),
57603            "Expected typed PARTITION BY property"
57604        );
57605        assert!(
57606            create
57607                .properties
57608                .iter()
57609                .any(|p| matches!(p, Expression::ClusterByColumnsProperty(_))),
57610            "Expected typed CLUSTER BY property"
57611        );
57612        assert!(
57613            create
57614                .properties
57615                .iter()
57616                .any(|p| matches!(p, Expression::OptionsProperty(_))),
57617            "Expected typed OPTIONS property"
57618        );
57619        assert!(
57620            !create
57621                .properties
57622                .iter()
57623                .any(|p| matches!(p, Expression::Raw(_))),
57624            "BigQuery table properties should not fall back to Raw"
57625        );
57626
57627        let options = create
57628            .properties
57629            .iter()
57630            .find_map(|p| match p {
57631                Expression::OptionsProperty(o) => Some(o),
57632                _ => None,
57633            })
57634            .expect("Expected OptionsProperty");
57635        assert_eq!(options.entries.len(), 2);
57636        assert_eq!(options.entries[0].key.name, "description");
57637        assert_eq!(options.entries[1].key.name, "labels");
57638    }
57639
57640    #[test]
57641    fn test_bigquery_create_table_properties_roundtrip() {
57642        use crate::DialectType;
57643
57644        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";
57645        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";
57646        let parsed = crate::parse(sql, DialectType::BigQuery).unwrap();
57647        let generated = crate::generate(&parsed[0], DialectType::BigQuery).unwrap();
57648        assert_eq!(generated, expected);
57649    }
57650
57651    #[test]
57652    fn test_parse_drop_table() {
57653        let result = Parser::parse_sql("DROP TABLE IF EXISTS users CASCADE").unwrap();
57654        assert!(matches!(result[0], Expression::DropTable(_)));
57655
57656        if let Expression::DropTable(dt) = &result[0] {
57657            assert!(dt.if_exists);
57658            assert!(dt.cascade);
57659            assert_eq!(dt.names.len(), 1);
57660        }
57661    }
57662
57663    #[test]
57664    fn test_parse_alter_table_add_column() {
57665        let result = Parser::parse_sql("ALTER TABLE users ADD COLUMN email VARCHAR(255)").unwrap();
57666        assert!(matches!(result[0], Expression::AlterTable(_)));
57667
57668        if let Expression::AlterTable(at) = &result[0] {
57669            assert_eq!(at.actions.len(), 1);
57670            assert!(matches!(at.actions[0], AlterTableAction::AddColumn { .. }));
57671        }
57672    }
57673
57674    #[test]
57675    fn test_parse_alter_table_drop_column() {
57676        let result = Parser::parse_sql("ALTER TABLE users DROP COLUMN email").unwrap();
57677        if let Expression::AlterTable(at) = &result[0] {
57678            assert!(matches!(at.actions[0], AlterTableAction::DropColumn { .. }));
57679        }
57680    }
57681
57682    #[test]
57683    fn test_tsql_alter_table_set_options() {
57684        use crate::{transpile, DialectType};
57685        let tests = vec![
57686            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=OFF)",
57687            "ALTER TABLE tbl SET (FILESTREAM_ON = 'test')",
57688            "ALTER TABLE tbl SET (DATA_DELETION=ON)",
57689            "ALTER TABLE tbl SET (DATA_DELETION=OFF)",
57690            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=ON(HISTORY_TABLE=db.tbl, DATA_CONSISTENCY_CHECK=OFF, HISTORY_RETENTION_PERIOD=5 DAYS))",
57691            "ALTER TABLE tbl SET (SYSTEM_VERSIONING=ON(HISTORY_TABLE=db.tbl, HISTORY_RETENTION_PERIOD=INFINITE))",
57692            "ALTER TABLE tbl SET (DATA_DELETION=ON(FILTER_COLUMN=col, RETENTION_PERIOD=5 MONTHS))",
57693        ];
57694        for sql in tests {
57695            let result = transpile(sql, DialectType::TSQL, DialectType::TSQL);
57696            match result {
57697                Ok(output) => {
57698                    assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
57699                }
57700                Err(e) => {
57701                    panic!("Parse/generate failed for: {} -- {:?}", sql, e);
57702                }
57703            }
57704        }
57705    }
57706
57707    #[test]
57708    fn test_parse_create_index() {
57709        let result = Parser::parse_sql("CREATE UNIQUE INDEX idx_email ON users (email)").unwrap();
57710        assert!(matches!(result[0], Expression::CreateIndex(_)));
57711
57712        if let Expression::CreateIndex(ci) = &result[0] {
57713            assert!(ci.unique);
57714            assert_eq!(ci.name.name, "idx_email");
57715            assert_eq!(ci.table.name.name, "users");
57716            assert_eq!(ci.columns.len(), 1);
57717        }
57718    }
57719
57720    #[test]
57721    fn test_parse_drop_index() {
57722        let result = Parser::parse_sql("DROP INDEX IF EXISTS idx_email ON users").unwrap();
57723        assert!(matches!(result[0], Expression::DropIndex(_)));
57724
57725        if let Expression::DropIndex(di) = &result[0] {
57726            assert!(di.if_exists);
57727            assert!(di.table.is_some());
57728        }
57729    }
57730
57731    #[test]
57732    fn test_parse_create_view() {
57733        let result =
57734            Parser::parse_sql("CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1")
57735                .unwrap();
57736        assert!(matches!(result[0], Expression::CreateView(_)));
57737    }
57738
57739    #[test]
57740    fn test_parse_create_materialized_view() {
57741        let result =
57742            Parser::parse_sql("CREATE MATERIALIZED VIEW stats AS SELECT COUNT(*) FROM users")
57743                .unwrap();
57744        if let Expression::CreateView(cv) = &result[0] {
57745            assert!(cv.materialized);
57746        }
57747    }
57748
57749    #[test]
57750    fn test_hive_stored_by() {
57751        use crate::{transpile, DialectType};
57752        let sql = "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'";
57753        let result = transpile(sql, DialectType::Hive, DialectType::Hive);
57754        match result {
57755            Ok(output) => {
57756                assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
57757            }
57758            Err(e) => {
57759                panic!("Parse/generate failed for: {} -- {:?}", sql, e);
57760            }
57761        }
57762    }
57763
57764    #[test]
57765    fn test_hive_row_format_serde() {
57766        use crate::{transpile, DialectType};
57767
57768        // Test various Hive CREATE TABLE syntax
57769        let test_cases = vec![
57770            (
57771                "CREATE TABLE my_table (a7 ARRAY<DATE>)",
57772                "CREATE TABLE my_table (a7 ARRAY<DATE>)",
57773            ),
57774            (
57775                "CREATE EXTERNAL TABLE my_table (x INT) ROW FORMAT SERDE 'a'",
57776                "CREATE EXTERNAL TABLE my_table (x INT) ROW FORMAT SERDE 'a'",
57777            ),
57778            (
57779                "CREATE EXTERNAL TABLE my_table (x INT) STORED AS INPUTFORMAT 'b' OUTPUTFORMAT 'c'",
57780                "CREATE EXTERNAL TABLE my_table (x INT) STORED AS INPUTFORMAT 'b' OUTPUTFORMAT 'c'",
57781            ),
57782            (
57783                "CREATE EXTERNAL TABLE my_table (x INT) LOCATION 'd'",
57784                "CREATE EXTERNAL TABLE my_table (x INT) LOCATION 'd'",
57785            ),
57786            (
57787                "CREATE EXTERNAL TABLE my_table (x INT) TBLPROPERTIES ('e'='f')",
57788                "CREATE EXTERNAL TABLE my_table (x INT) TBLPROPERTIES ('e'='f')",
57789            ),
57790            (
57791                "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'",
57792                "CREATE EXTERNAL TABLE X (y INT) STORED BY 'x'",
57793            ),
57794        ];
57795
57796        for (sql, expected) in &test_cases {
57797            let result = transpile(sql, DialectType::Hive, DialectType::Hive);
57798            match result {
57799                Ok(output) => {
57800                    assert_eq!(output[0].trim(), *expected, "Identity failed for: {}", sql);
57801                }
57802                Err(e) => {
57803                    panic!("Parse/generate failed for: {} -- {:?}", sql, e);
57804                }
57805            }
57806        }
57807
57808        // Test full case with all Hive table properties
57809        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')";
57810        let result = transpile(sql, DialectType::Hive, DialectType::Hive);
57811        match result {
57812            Ok(output) => {
57813                assert_eq!(output[0].trim(), sql, "Identity failed for: {}", sql);
57814            }
57815            Err(e) => {
57816                panic!("Parse/generate failed for: {} -- {:?}", sql, e);
57817            }
57818        }
57819    }
57820
57821    #[test]
57822    fn test_parse_drop_view() {
57823        let result = Parser::parse_sql("DROP VIEW IF EXISTS active_users").unwrap();
57824        assert!(matches!(result[0], Expression::DropView(_)));
57825    }
57826
57827    #[test]
57828    fn test_parse_truncate() {
57829        let result = Parser::parse_sql("TRUNCATE TABLE users CASCADE").unwrap();
57830        assert!(matches!(result[0], Expression::Truncate(_)));
57831
57832        if let Expression::Truncate(tr) = &result[0] {
57833            assert!(tr.cascade);
57834        }
57835    }
57836
57837    // Tests for typed aggregate functions
57838    #[test]
57839    fn test_parse_typed_aggregates() {
57840        // COUNT with DISTINCT
57841        let result = Parser::parse_sql("SELECT COUNT(DISTINCT user_id)").unwrap();
57842        let select = result[0].as_select().unwrap();
57843        if let Expression::Count(c) = &select.expressions[0] {
57844            assert!(c.distinct);
57845            assert!(!c.star);
57846        } else {
57847            panic!("Expected Count expression");
57848        }
57849
57850        // AVG
57851        let result = Parser::parse_sql("SELECT AVG(price)").unwrap();
57852        let select = result[0].as_select().unwrap();
57853        assert!(matches!(select.expressions[0], Expression::Avg(_)));
57854
57855        // MIN/MAX
57856        let result = Parser::parse_sql("SELECT MIN(a), MAX(b)").unwrap();
57857        let select = result[0].as_select().unwrap();
57858        assert!(matches!(select.expressions[0], Expression::Min(_)));
57859        assert!(matches!(select.expressions[1], Expression::Max(_)));
57860
57861        // STDDEV/VARIANCE
57862        let result = Parser::parse_sql("SELECT STDDEV(x), VARIANCE(y)").unwrap();
57863        let select = result[0].as_select().unwrap();
57864        assert!(matches!(select.expressions[0], Expression::Stddev(_)));
57865        assert!(matches!(select.expressions[1], Expression::Variance(_)));
57866    }
57867
57868    #[test]
57869    fn test_parse_typed_window_functions() {
57870        // ROW_NUMBER
57871        let result = Parser::parse_sql("SELECT ROW_NUMBER() OVER (ORDER BY id)").unwrap();
57872        let select = result[0].as_select().unwrap();
57873        if let Expression::WindowFunction(wf) = &select.expressions[0] {
57874            assert!(matches!(wf.this, Expression::RowNumber(_)));
57875        } else {
57876            panic!("Expected WindowFunction");
57877        }
57878
57879        // RANK and DENSE_RANK
57880        let result = Parser::parse_sql("SELECT RANK() OVER (), DENSE_RANK() OVER ()").unwrap();
57881        let select = result[0].as_select().unwrap();
57882        if let Expression::WindowFunction(wf) = &select.expressions[0] {
57883            assert!(matches!(wf.this, Expression::Rank(_)));
57884        }
57885        if let Expression::WindowFunction(wf) = &select.expressions[1] {
57886            assert!(matches!(wf.this, Expression::DenseRank(_)));
57887        }
57888
57889        // LEAD/LAG
57890        let result = Parser::parse_sql("SELECT LEAD(val, 1, 0) OVER (ORDER BY id)").unwrap();
57891        let select = result[0].as_select().unwrap();
57892        if let Expression::WindowFunction(wf) = &select.expressions[0] {
57893            if let Expression::Lead(f) = &wf.this {
57894                assert!(f.offset.is_some());
57895                assert!(f.default.is_some());
57896            } else {
57897                panic!("Expected Lead");
57898            }
57899        }
57900
57901        // NTILE
57902        let result = Parser::parse_sql("SELECT NTILE(4) OVER (ORDER BY score)").unwrap();
57903        let select = result[0].as_select().unwrap();
57904        if let Expression::WindowFunction(wf) = &select.expressions[0] {
57905            assert!(matches!(wf.this, Expression::NTile(_)));
57906        }
57907    }
57908
57909    #[test]
57910    fn test_parse_string_functions() {
57911        // CONTAINS, STARTS_WITH, ENDS_WITH
57912        let result = Parser::parse_sql("SELECT CONTAINS(name, 'test')").unwrap();
57913        let select = result[0].as_select().unwrap();
57914        assert!(matches!(select.expressions[0], Expression::Contains(_)));
57915
57916        let result = Parser::parse_sql("SELECT STARTS_WITH(name, 'A')").unwrap();
57917        let select = result[0].as_select().unwrap();
57918        assert!(matches!(select.expressions[0], Expression::StartsWith(_)));
57919
57920        let result = Parser::parse_sql("SELECT ENDS_WITH(name, 'z')").unwrap();
57921        let select = result[0].as_select().unwrap();
57922        assert!(matches!(select.expressions[0], Expression::EndsWith(_)));
57923    }
57924
57925    #[test]
57926    fn test_parse_math_functions() {
57927        // MOD function
57928        let result = Parser::parse_sql("SELECT MOD(10, 3)").unwrap();
57929        let select = result[0].as_select().unwrap();
57930        assert!(matches!(select.expressions[0], Expression::ModFunc(_)));
57931
57932        // RANDOM and RAND
57933        let result = Parser::parse_sql("SELECT RANDOM()").unwrap();
57934        let select = result[0].as_select().unwrap();
57935        assert!(matches!(select.expressions[0], Expression::Random(_)));
57936
57937        let result = Parser::parse_sql("SELECT RAND(42)").unwrap();
57938        let select = result[0].as_select().unwrap();
57939        assert!(matches!(select.expressions[0], Expression::Rand(_)));
57940
57941        // Trigonometric functions
57942        let result = Parser::parse_sql("SELECT SIN(x), COS(x), TAN(x)").unwrap();
57943        let select = result[0].as_select().unwrap();
57944        assert!(matches!(select.expressions[0], Expression::Sin(_)));
57945        assert!(matches!(select.expressions[1], Expression::Cos(_)));
57946        assert!(matches!(select.expressions[2], Expression::Tan(_)));
57947    }
57948
57949    #[test]
57950    fn test_parse_date_functions() {
57951        // Date part extraction functions
57952        let result =
57953            Parser::parse_sql("SELECT YEAR(date_col), MONTH(date_col), DAY(date_col)").unwrap();
57954        let select = result[0].as_select().unwrap();
57955        assert!(matches!(select.expressions[0], Expression::Year(_)));
57956        assert!(matches!(select.expressions[1], Expression::Month(_)));
57957        assert!(matches!(select.expressions[2], Expression::Day(_)));
57958
57959        // EPOCH and EPOCH_MS
57960        let result = Parser::parse_sql("SELECT EPOCH(ts), EPOCH_MS(ts)").unwrap();
57961        let select = result[0].as_select().unwrap();
57962        assert!(matches!(select.expressions[0], Expression::Epoch(_)));
57963        assert!(matches!(select.expressions[1], Expression::EpochMs(_)));
57964    }
57965
57966    #[test]
57967    fn test_parse_array_functions() {
57968        // ARRAY_LENGTH
57969        let result = Parser::parse_sql("SELECT ARRAY_LENGTH(arr)").unwrap();
57970        let select = result[0].as_select().unwrap();
57971        assert!(matches!(select.expressions[0], Expression::ArrayLength(_)));
57972
57973        // ARRAY_CONTAINS
57974        let result = Parser::parse_sql("SELECT ARRAY_CONTAINS(arr, 1)").unwrap();
57975        let select = result[0].as_select().unwrap();
57976        assert!(matches!(
57977            select.expressions[0],
57978            Expression::ArrayContains(_)
57979        ));
57980
57981        // EXPLODE
57982        let result = Parser::parse_sql("SELECT EXPLODE(arr)").unwrap();
57983        let select = result[0].as_select().unwrap();
57984        assert!(matches!(select.expressions[0], Expression::Explode(_)));
57985    }
57986
57987    #[test]
57988    fn test_parse_json_functions() {
57989        // JSON_EXTRACT
57990        let result = Parser::parse_sql("SELECT JSON_EXTRACT(data, '$.name')").unwrap();
57991        let select = result[0].as_select().unwrap();
57992        assert!(matches!(select.expressions[0], Expression::JsonExtract(_)));
57993
57994        // JSON_ARRAY_LENGTH
57995        let result = Parser::parse_sql("SELECT JSON_ARRAY_LENGTH(arr)").unwrap();
57996        let select = result[0].as_select().unwrap();
57997        assert!(matches!(
57998            select.expressions[0],
57999            Expression::JsonArrayLength(_)
58000        ));
58001
58002        // TO_JSON and PARSE_JSON
58003        let result = Parser::parse_sql("SELECT TO_JSON(obj), PARSE_JSON(str)").unwrap();
58004        let select = result[0].as_select().unwrap();
58005        assert!(matches!(select.expressions[0], Expression::ToJson(_)));
58006        assert!(matches!(select.expressions[1], Expression::ParseJson(_)));
58007
58008        // JSON literal: JSON '"foo"' -> ParseJson
58009        let result = Parser::parse_sql("SELECT JSON '\"foo\"'").unwrap();
58010        let select = result[0].as_select().unwrap();
58011        assert!(
58012            matches!(select.expressions[0], Expression::ParseJson(_)),
58013            "Expected ParseJson, got: {:?}",
58014            select.expressions[0]
58015        );
58016    }
58017
58018    #[test]
58019    fn test_parse_map_functions() {
58020        // MAP_KEYS and MAP_VALUES
58021        let result = Parser::parse_sql("SELECT MAP_KEYS(m), MAP_VALUES(m)").unwrap();
58022        let select = result[0].as_select().unwrap();
58023        assert!(matches!(select.expressions[0], Expression::MapKeys(_)));
58024        assert!(matches!(select.expressions[1], Expression::MapValues(_)));
58025
58026        // ELEMENT_AT
58027        let result = Parser::parse_sql("SELECT ELEMENT_AT(m, 'key')").unwrap();
58028        let select = result[0].as_select().unwrap();
58029        assert!(matches!(select.expressions[0], Expression::ElementAt(_)));
58030    }
58031
58032    #[test]
58033    fn test_parse_date_literals() {
58034        // DATE literal (generic mode normalizes to CAST)
58035        let result = Parser::parse_sql("SELECT DATE '2024-01-15'").unwrap();
58036        let select = result[0].as_select().unwrap();
58037        match &select.expressions[0] {
58038            Expression::Cast(cast) => {
58039                match &cast.this {
58040                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
58041                        let Literal::String(s) = lit.as_ref() else {
58042                            unreachable!()
58043                        };
58044                        assert_eq!(s, "2024-01-15")
58045                    }
58046                    other => panic!("Expected String literal in Cast, got {:?}", other),
58047                }
58048                assert!(matches!(cast.to, DataType::Date));
58049            }
58050            other => panic!("Expected Cast expression, got {:?}", other),
58051        }
58052
58053        // TIME literal
58054        let result = Parser::parse_sql("SELECT TIME '10:30:00'").unwrap();
58055        let select = result[0].as_select().unwrap();
58056        match &select.expressions[0] {
58057            Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Time(_)) => {
58058                let Literal::Time(t) = lit.as_ref() else {
58059                    unreachable!()
58060                };
58061                assert_eq!(t, "10:30:00");
58062            }
58063            _ => panic!("Expected Time literal"),
58064        }
58065
58066        // TIMESTAMP literal -> CAST in generic mode
58067        let result = Parser::parse_sql("SELECT TIMESTAMP '2024-01-15 10:30:00'").unwrap();
58068        let select = result[0].as_select().unwrap();
58069        match &select.expressions[0] {
58070            Expression::Cast(cast) => {
58071                match &cast.this {
58072                    Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
58073                        let Literal::String(s) = lit.as_ref() else {
58074                            unreachable!()
58075                        };
58076                        assert_eq!(s, "2024-01-15 10:30:00")
58077                    }
58078                    other => panic!("Expected String literal inside Cast, got {:?}", other),
58079                }
58080                assert!(matches!(
58081                    &cast.to,
58082                    DataType::Timestamp {
58083                        precision: None,
58084                        timezone: false
58085                    }
58086                ));
58087            }
58088            _ => panic!("Expected Cast expression for TIMESTAMP literal"),
58089        }
58090    }
58091
58092    #[test]
58093    fn test_parse_star_exclude() {
58094        // EXCLUDE with multiple columns
58095        let result = Parser::parse_sql("SELECT * EXCLUDE (col1, col2) FROM t").unwrap();
58096        let select = result[0].as_select().unwrap();
58097        if let Expression::Star(star) = &select.expressions[0] {
58098            assert!(star.except.is_some());
58099            let except = star.except.as_ref().unwrap();
58100            assert_eq!(except.len(), 2);
58101            assert_eq!(except[0].name, "col1");
58102            assert_eq!(except[1].name, "col2");
58103        } else {
58104            panic!("Expected Star expression");
58105        }
58106
58107        // EXCEPT (BigQuery syntax)
58108        let result = Parser::parse_sql("SELECT * EXCEPT (id, created_at) FROM t").unwrap();
58109        let select = result[0].as_select().unwrap();
58110        if let Expression::Star(star) = &select.expressions[0] {
58111            assert!(star.except.is_some());
58112        } else {
58113            panic!("Expected Star expression");
58114        }
58115
58116        // table.* with EXCLUDE
58117        let result = Parser::parse_sql("SELECT t.* EXCLUDE (col1) FROM t").unwrap();
58118        let select = result[0].as_select().unwrap();
58119        if let Expression::Star(star) = &select.expressions[0] {
58120            assert!(star.table.is_some());
58121            assert_eq!(star.table.as_ref().unwrap().name, "t");
58122            assert!(star.except.is_some());
58123        } else {
58124            panic!("Expected Star expression");
58125        }
58126    }
58127
58128    #[test]
58129    fn test_parse_star_replace() {
58130        // REPLACE with single expression
58131        let result = Parser::parse_sql("SELECT * REPLACE (UPPER(name) AS name) FROM t").unwrap();
58132        let select = result[0].as_select().unwrap();
58133        if let Expression::Star(star) = &select.expressions[0] {
58134            assert!(star.replace.is_some());
58135            let replace = star.replace.as_ref().unwrap();
58136            assert_eq!(replace.len(), 1);
58137            assert_eq!(replace[0].alias.name, "name");
58138        } else {
58139            panic!("Expected Star expression");
58140        }
58141
58142        // REPLACE with multiple expressions
58143        let result = Parser::parse_sql("SELECT * REPLACE (a + 1 AS a, b * 2 AS b) FROM t").unwrap();
58144        let select = result[0].as_select().unwrap();
58145        if let Expression::Star(star) = &select.expressions[0] {
58146            let replace = star.replace.as_ref().unwrap();
58147            assert_eq!(replace.len(), 2);
58148        } else {
58149            panic!("Expected Star expression");
58150        }
58151    }
58152
58153    #[test]
58154    fn test_parse_star_rename() {
58155        // RENAME with multiple columns
58156        let result =
58157            Parser::parse_sql("SELECT * RENAME (old_col AS new_col, x AS y) FROM t").unwrap();
58158        let select = result[0].as_select().unwrap();
58159        if let Expression::Star(star) = &select.expressions[0] {
58160            assert!(star.rename.is_some());
58161            let rename = star.rename.as_ref().unwrap();
58162            assert_eq!(rename.len(), 2);
58163            assert_eq!(rename[0].0.name, "old_col");
58164            assert_eq!(rename[0].1.name, "new_col");
58165        } else {
58166            panic!("Expected Star expression");
58167        }
58168    }
58169
58170    #[test]
58171    fn test_parse_star_combined() {
58172        // EXCLUDE + REPLACE combined
58173        let result =
58174            Parser::parse_sql("SELECT * EXCLUDE (id) REPLACE (name || '!' AS name) FROM t")
58175                .unwrap();
58176        let select = result[0].as_select().unwrap();
58177        if let Expression::Star(star) = &select.expressions[0] {
58178            assert!(star.except.is_some());
58179            assert!(star.replace.is_some());
58180        } else {
58181            panic!("Expected Star expression");
58182        }
58183    }
58184
58185    #[test]
58186    fn test_parse_spatial_types() {
58187        // GEOMETRY with subtype and SRID (PostgreSQL syntax)
58188        let result = Parser::parse_sql("CREATE TABLE t (geom GEOMETRY(Point, 4326))").unwrap();
58189        if let Expression::CreateTable(ct) = &result[0] {
58190            assert_eq!(ct.columns.len(), 1);
58191            match &ct.columns[0].data_type {
58192                DataType::Geometry { subtype, srid } => {
58193                    assert_eq!(subtype.as_deref(), Some("POINT"));
58194                    assert_eq!(*srid, Some(4326));
58195                }
58196                _ => panic!("Expected Geometry type"),
58197            }
58198        }
58199
58200        // GEOGRAPHY without parameters
58201        let result = Parser::parse_sql("CREATE TABLE t (loc GEOGRAPHY)").unwrap();
58202        if let Expression::CreateTable(ct) = &result[0] {
58203            match &ct.columns[0].data_type {
58204                DataType::Geography { subtype, srid } => {
58205                    assert!(subtype.is_none());
58206                    assert!(srid.is_none());
58207                }
58208                _ => panic!("Expected Geography type"),
58209            }
58210        }
58211
58212        // GEOMETRY subtype only (no SRID)
58213        let result = Parser::parse_sql("CREATE TABLE t (geom GEOMETRY(LineString))").unwrap();
58214        if let Expression::CreateTable(ct) = &result[0] {
58215            match &ct.columns[0].data_type {
58216                DataType::Geometry { subtype, srid } => {
58217                    assert_eq!(subtype.as_deref(), Some("LINESTRING"));
58218                    assert!(srid.is_none());
58219                }
58220                _ => panic!("Expected Geometry type"),
58221            }
58222        }
58223
58224        // Simple POINT type (MySQL-style without SRID)
58225        let result = Parser::parse_sql("CREATE TABLE t (pt POINT)").unwrap();
58226        if let Expression::CreateTable(ct) = &result[0] {
58227            match &ct.columns[0].data_type {
58228                DataType::Geometry { subtype, srid } => {
58229                    assert_eq!(subtype.as_deref(), Some("POINT"));
58230                    assert!(srid.is_none());
58231                }
58232                _ => panic!("Expected Geometry type"),
58233            }
58234        }
58235    }
58236
58237    #[test]
58238    fn test_parse_duckdb_pivot_simple() {
58239        let sql = "PIVOT Cities ON Year USING SUM(Population)";
58240        let result = Parser::parse_sql(sql);
58241        assert!(
58242            result.is_ok(),
58243            "Failed to parse: {} - {:?}",
58244            sql,
58245            result.err()
58246        );
58247        let stmts = result.unwrap();
58248        assert_eq!(
58249            stmts.len(),
58250            1,
58251            "Expected 1 statement, got {}: {:?}",
58252            stmts.len(),
58253            stmts
58254        );
58255        match &stmts[0] {
58256            Expression::Pivot(p) => {
58257                assert!(!p.unpivot);
58258                assert!(!p.expressions.is_empty(), "Should have ON expressions");
58259                assert!(!p.using.is_empty(), "Should have USING expressions");
58260            }
58261            other => panic!("Expected Pivot, got {:?}", other),
58262        }
58263    }
58264
58265    #[test]
58266    fn test_parse_duckdb_pivot_with_group_by() {
58267        let sql = "PIVOT Cities ON Year USING SUM(Population) GROUP BY Country";
58268        let result = Parser::parse_sql(sql);
58269        assert!(
58270            result.is_ok(),
58271            "Failed to parse: {} - {:?}",
58272            sql,
58273            result.err()
58274        );
58275    }
58276
58277    #[test]
58278    fn test_parse_duckdb_unpivot() {
58279        let sql = "UNPIVOT monthly_sales ON jan, feb, mar INTO NAME month VALUE sales";
58280        let result = Parser::parse_sql(sql);
58281        assert!(
58282            result.is_ok(),
58283            "Failed to parse: {} - {:?}",
58284            sql,
58285            result.err()
58286        );
58287    }
58288
58289    #[test]
58290    fn test_parse_standard_pivot_in_from() {
58291        let sql = "SELECT * FROM cities PIVOT(SUM(population) FOR year IN (2000, 2010, 2020))";
58292        let result = Parser::parse_sql(sql);
58293        assert!(
58294            result.is_ok(),
58295            "Failed to parse: {} - {:?}",
58296            sql,
58297            result.err()
58298        );
58299    }
58300
58301    fn assert_pivot_roundtrip(sql: &str) {
58302        let parsed = crate::parse(sql, crate::DialectType::DuckDB);
58303        assert!(
58304            parsed.is_ok(),
58305            "Failed to parse: {} - {:?}",
58306            sql,
58307            parsed.err()
58308        );
58309        let stmts = parsed.unwrap();
58310        assert_eq!(stmts.len(), 1, "Expected 1 statement for: {}", sql);
58311        let generated = crate::generate(&stmts[0], crate::DialectType::DuckDB);
58312        assert!(
58313            generated.is_ok(),
58314            "Failed to generate: {} - {:?}",
58315            sql,
58316            generated.err()
58317        );
58318        let result = generated.unwrap();
58319        assert_eq!(result.trim(), sql, "Round-trip mismatch for: {}", sql);
58320    }
58321
58322    fn assert_pivot_roundtrip_bq(sql: &str) {
58323        let parsed = crate::parse(sql, crate::DialectType::BigQuery);
58324        assert!(
58325            parsed.is_ok(),
58326            "Failed to parse: {} - {:?}",
58327            sql,
58328            parsed.err()
58329        );
58330        let stmts = parsed.unwrap();
58331        assert_eq!(stmts.len(), 1, "Expected 1 statement for: {}", sql);
58332        let generated = crate::generate(&stmts[0], crate::DialectType::BigQuery);
58333        assert!(
58334            generated.is_ok(),
58335            "Failed to generate: {} - {:?}",
58336            sql,
58337            generated.err()
58338        );
58339        let result = generated.unwrap();
58340        assert_eq!(result.trim(), sql, "Round-trip mismatch for: {}", sql);
58341    }
58342
58343    #[test]
58344    fn test_pivot_roundtrip_duckdb_simple() {
58345        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population)");
58346    }
58347
58348    #[test]
58349    fn test_pivot_roundtrip_duckdb_group_by() {
58350        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) GROUP BY Country");
58351    }
58352
58353    #[test]
58354    fn test_pivot_roundtrip_duckdb_in_clause() {
58355        assert_pivot_roundtrip(
58356            "PIVOT Cities ON Year IN (2000, 2010) USING SUM(Population) GROUP BY Country",
58357        );
58358    }
58359
58360    #[test]
58361    fn test_pivot_roundtrip_duckdb_multiple_using() {
58362        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) AS total, MAX(Population) AS max GROUP BY Country");
58363    }
58364
58365    #[test]
58366    fn test_pivot_roundtrip_duckdb_multiple_on() {
58367        assert_pivot_roundtrip("PIVOT Cities ON Country, Name USING SUM(Population)");
58368    }
58369
58370    #[test]
58371    fn test_pivot_roundtrip_duckdb_concat_on() {
58372        assert_pivot_roundtrip("PIVOT Cities ON Country || '_' || Name USING SUM(Population)");
58373    }
58374
58375    #[test]
58376    fn test_pivot_roundtrip_duckdb_multiple_group_by() {
58377        assert_pivot_roundtrip("PIVOT Cities ON Year USING SUM(Population) GROUP BY Country, Name");
58378    }
58379
58380    #[test]
58381    fn test_pivot_roundtrip_duckdb_first() {
58382        assert_pivot_roundtrip("PIVOT Cities ON Year USING FIRST(Population)");
58383    }
58384
58385    #[test]
58386    fn test_unpivot_roundtrip_duckdb_basic() {
58387        assert_pivot_roundtrip(
58388            "UNPIVOT monthly_sales ON jan, feb, mar, apr, may, jun INTO NAME month VALUE sales",
58389        );
58390    }
58391
58392    #[test]
58393    fn test_unpivot_roundtrip_duckdb_subquery() {
58394        assert_pivot_roundtrip("UNPIVOT (SELECT 1 AS col1, 2 AS col2) ON foo, bar");
58395    }
58396
58397    #[test]
58398    fn test_pivot_roundtrip_duckdb_cte() {
58399        assert_pivot_roundtrip("WITH pivot_alias AS (PIVOT Cities ON Year USING SUM(Population) GROUP BY Country) SELECT * FROM pivot_alias");
58400    }
58401
58402    #[test]
58403    fn test_pivot_roundtrip_duckdb_subquery() {
58404        assert_pivot_roundtrip("SELECT * FROM (PIVOT Cities ON Year USING SUM(Population) GROUP BY Country) AS pivot_alias");
58405    }
58406
58407    #[test]
58408    fn test_pivot_roundtrip_standard_from() {
58409        assert_pivot_roundtrip("SELECT * FROM cities PIVOT(SUM(population) FOR year IN (2000, 2010, 2020) GROUP BY country)");
58410    }
58411
58412    #[test]
58413    fn test_pivot_roundtrip_standard_bare_in() {
58414        assert_pivot_roundtrip("SELECT * FROM t PIVOT(SUM(y) FOR foo IN y_enum)");
58415    }
58416
58417    #[test]
58418    fn test_unpivot_roundtrip_bigquery() {
58419        assert_pivot_roundtrip_bq("SELECT * FROM q UNPIVOT(values FOR quarter IN (b, c))");
58420    }
58421
58422    #[test]
58423    fn test_pivot_roundtrip_bigquery_aliases() {
58424        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))");
58425    }
58426
58427    #[test]
58428    fn test_unpivot_roundtrip_bigquery_parens() {
58429        assert_pivot_roundtrip_bq(
58430            "SELECT * FROM (SELECT * FROM `t`) AS a UNPIVOT((c) FOR c_name IN (v1, v2))",
58431        );
58432    }
58433
58434    #[test]
58435    fn test_pivot_roundtrip_bigquery_multi_agg() {
58436        // Note: BigQuery fixture expects implicit aliases to become explicit AS
58437        let sql = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) AS d, COUNT(*) AS e FOR c IN ('x', 'y'))";
58438        assert_pivot_roundtrip_bq(sql);
58439    }
58440
58441    // Additional fixture tests for UNPIVOT with COLUMNS and grouped ON
58442    #[test]
58443    fn test_unpivot_roundtrip_duckdb_columns_exclude() {
58444        assert_pivot_roundtrip(
58445            "UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales",
58446        );
58447    }
58448
58449    #[test]
58450    fn test_unpivot_roundtrip_duckdb_grouped_columns() {
58451        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");
58452    }
58453
58454    #[test]
58455    fn test_unpivot_roundtrip_duckdb_cte_columns() {
58456        assert_pivot_roundtrip("WITH unpivot_alias AS (UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales) SELECT * FROM unpivot_alias");
58457    }
58458
58459    #[test]
58460    fn test_unpivot_roundtrip_duckdb_subquery_columns() {
58461        assert_pivot_roundtrip("SELECT * FROM (UNPIVOT monthly_sales ON COLUMNS(* EXCLUDE (empid, dept)) INTO NAME month VALUE sales) AS unpivot_alias");
58462    }
58463
58464    #[test]
58465    fn test_pivot_roundtrip_duckdb_cte_with_columns() {
58466        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)");
58467    }
58468
58469    #[test]
58470    fn test_pivot_roundtrip_standard_first_with_alias() {
58471        // DuckDB fixture #73: comma before FOR is dropped in expected output
58472        let sql = "SELECT * FROM t PIVOT(FIRST(t) AS t, FOR quarter IN ('Q1', 'Q2'))";
58473        let expected = "SELECT * FROM t PIVOT(FIRST(t) AS t FOR quarter IN ('Q1', 'Q2'))";
58474        let parsed = crate::parse(sql, crate::DialectType::DuckDB);
58475        assert!(
58476            parsed.is_ok(),
58477            "Failed to parse: {} - {:?}",
58478            sql,
58479            parsed.err()
58480        );
58481        let stmts = parsed.unwrap();
58482        assert_eq!(stmts.len(), 1);
58483        let generated = crate::generate(&stmts[0], crate::DialectType::DuckDB);
58484        assert!(
58485            generated.is_ok(),
58486            "Failed to generate: {} - {:?}",
58487            sql,
58488            generated.err()
58489        );
58490        let result = generated.unwrap();
58491        assert_eq!(result.trim(), expected, "Round-trip mismatch");
58492    }
58493
58494    #[test]
58495    fn test_pivot_roundtrip_bigquery_implicit_alias() {
58496        // BigQuery fixture #134: implicit aliases become explicit AS
58497        let sql = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) d, COUNT(*) e FOR c IN ('x', 'y'))";
58498        let expected = "SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) AS d, COUNT(*) AS e FOR c IN ('x', 'y'))";
58499        let parsed = crate::parse(sql, crate::DialectType::BigQuery);
58500        assert!(
58501            parsed.is_ok(),
58502            "Failed to parse: {} - {:?}",
58503            sql,
58504            parsed.err()
58505        );
58506        let stmts = parsed.unwrap();
58507        assert_eq!(stmts.len(), 1);
58508        let generated = crate::generate(&stmts[0], crate::DialectType::BigQuery);
58509        assert!(
58510            generated.is_ok(),
58511            "Failed to generate: {} - {:?}",
58512            sql,
58513            generated.err()
58514        );
58515        let result = generated.unwrap();
58516        assert_eq!(result.trim(), expected, "Round-trip mismatch");
58517    }
58518
58519    #[test]
58520    fn test_duckdb_struct_enum_union_row_types() {
58521        use crate::DialectType;
58522
58523        // Helper to test roundtrip with DuckDB dialect - runs in a thread with larger stack
58524        fn check(sql: &str, expected: Option<&str>) {
58525            let sql = sql.to_string();
58526            let expected = expected.map(|s| s.to_string());
58527            let result = std::thread::Builder::new()
58528                .stack_size(16 * 1024 * 1024) // 16MB stack
58529                .spawn(move || {
58530                    let expected_out = expected.as_deref().unwrap_or(&sql);
58531                    let parsed = crate::parse(&sql, DialectType::DuckDB);
58532                    assert!(
58533                        parsed.is_ok(),
58534                        "Failed to parse: {} - {:?}",
58535                        sql,
58536                        parsed.err()
58537                    );
58538                    let stmts = parsed.unwrap();
58539                    assert!(!stmts.is_empty(), "No statements parsed: {}", sql);
58540                    let generated = crate::generate(&stmts[0], DialectType::DuckDB);
58541                    assert!(
58542                        generated.is_ok(),
58543                        "Failed to generate: {} - {:?}",
58544                        sql,
58545                        generated.err()
58546                    );
58547                    let result = generated.unwrap();
58548                    assert_eq!(result.trim(), expected_out, "Mismatch for: {}", sql);
58549                })
58550                .expect("Failed to spawn test thread")
58551                .join();
58552            assert!(result.is_ok(), "Test thread panicked");
58553        }
58554
58555        // UNION type
58556        check("CREATE TABLE tbl1 (u UNION(num INT, str TEXT))", None);
58557        // ENUM type
58558        check(
58559            "CREATE TABLE color (name ENUM('RED', 'GREEN', 'BLUE'))",
58560            None,
58561        );
58562        // ROW type -> STRUCT
58563        check(
58564            "SELECT CAST(ROW(1, 2) AS ROW(a INTEGER, b INTEGER))",
58565            Some("SELECT CAST(ROW(1, 2) AS STRUCT(a INT, b INT))"),
58566        );
58567        // STRUCT with parens
58568        check("CAST(x AS STRUCT(number BIGINT))", None);
58569        // STRUCT with quoted field names
58570        check(
58571            "CAST({'i': 1, 's': 'foo'} AS STRUCT(\"s\" TEXT, \"i\" INT))",
58572            None,
58573        );
58574        // Nested STRUCT
58575        check(
58576            "CAST(ROW(1, ROW(1)) AS STRUCT(number BIGINT, row STRUCT(number BIGINT)))",
58577            None,
58578        );
58579        // STRUCT with array suffix - test just the type parsing part
58580        // Note: STRUCT_PACK -> struct literal transform is a separate feature
58581        check("CAST(x AS STRUCT(a BIGINT)[][])", None);
58582        check("CAST(x AS STRUCT(a BIGINT)[])", None);
58583        // Double-colon cast with STRUCT type
58584        check("CAST({'a': 'b'} AS STRUCT(a TEXT))", None);
58585    }
58586
58587    // Helper for roundtrip identity tests
58588    fn roundtrip(sql: &str) -> String {
58589        let ast =
58590            Parser::parse_sql(sql).unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
58591        crate::generator::Generator::sql(&ast[0])
58592            .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e))
58593    }
58594
58595    fn assert_roundtrip(sql: &str) {
58596        let result = roundtrip(sql);
58597        assert_eq!(result, sql, "\n  Input:    {}\n  Output:   {}", sql, result);
58598    }
58599
58600    fn assert_roundtrip_expected(sql: &str, expected: &str) {
58601        let result = roundtrip(sql);
58602        assert_eq!(
58603            result, expected,
58604            "\n  Input:    {}\n  Expected: {}\n  Output:   {}",
58605            sql, expected, result
58606        );
58607    }
58608
58609    #[test]
58610    fn test_xmlelement_basic() {
58611        assert_roundtrip("SELECT XMLELEMENT(NAME foo)");
58612    }
58613
58614    #[test]
58615    fn test_xmlelement_with_xmlattributes() {
58616        assert_roundtrip("SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('xyz' AS bar))");
58617    }
58618
58619    #[test]
58620    fn test_xmlelement_with_multiple_attrs() {
58621        assert_roundtrip("SELECT XMLELEMENT(NAME test, XMLATTRIBUTES(a, b)) FROM test");
58622    }
58623
58624    #[test]
58625    fn test_xmlelement_with_content() {
58626        assert_roundtrip(
58627            "SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES(CURRENT_DATE AS bar), 'cont', 'ent')",
58628        );
58629    }
58630
58631    #[test]
58632    fn test_xmlelement_nested() {
58633        assert_roundtrip("SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('xyz' AS bar), XMLELEMENT(NAME abc), XMLCOMMENT('test'), XMLELEMENT(NAME xyz))");
58634    }
58635
58636    #[test]
58637    fn test_on_conflict_do_update() {
58638        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");
58639    }
58640
58641    #[test]
58642    fn test_on_conflict_do_nothing() {
58643        // ON CONFLICT(id) is the canonical form (no space before paren)
58644        assert_roundtrip_expected(
58645            "INSERT INTO test (id, name) VALUES (1, 'test') ON CONFLICT (id) DO NOTHING",
58646            "INSERT INTO test (id, name) VALUES (1, 'test') ON CONFLICT(id) DO NOTHING",
58647        );
58648    }
58649
58650    #[test]
58651    fn test_truncate_restart_identity() {
58652        assert_roundtrip("TRUNCATE TABLE t1 RESTART IDENTITY");
58653    }
58654
58655    #[test]
58656    fn test_truncate_restart_identity_restrict() {
58657        assert_roundtrip("TRUNCATE TABLE t1 RESTART IDENTITY RESTRICT");
58658    }
58659
58660    #[test]
58661    fn test_insert_by_name() {
58662        assert_roundtrip("INSERT INTO x BY NAME SELECT 1 AS y");
58663    }
58664
58665    #[test]
58666    fn test_insert_default_values_returning() {
58667        assert_roundtrip("INSERT INTO t DEFAULT VALUES RETURNING (c1)");
58668    }
58669
58670    #[test]
58671    fn test_union_all_by_name() {
58672        assert_roundtrip("SELECT 1 AS x UNION ALL BY NAME SELECT 2 AS x");
58673    }
58674
58675    #[test]
58676    fn test_minus_as_except() {
58677        // MINUS is Oracle/Redshift syntax for EXCEPT
58678        assert_roundtrip_expected(
58679            "SELECT foo, bar FROM table_1 MINUS SELECT foo, bar FROM table_2",
58680            "SELECT foo, bar FROM table_1 EXCEPT SELECT foo, bar FROM table_2",
58681        );
58682    }
58683
58684    #[test]
58685    fn test_filter_without_where() {
58686        assert_roundtrip_expected(
58687            "SELECT SUM(x) FILTER (x = 1)",
58688            "SELECT SUM(x) FILTER(WHERE x = 1)",
58689        );
58690    }
58691
58692    #[test]
58693    fn test_comment_on_materialized_view() {
58694        assert_roundtrip("COMMENT ON MATERIALIZED VIEW my_view IS 'this'");
58695    }
58696
58697    #[test]
58698    fn test_create_index_concurrently() {
58699        assert_roundtrip("CREATE INDEX CONCURRENTLY idx ON t(c)");
58700    }
58701
58702    #[test]
58703    fn test_create_index_if_not_exists() {
58704        assert_roundtrip("CREATE INDEX IF NOT EXISTS idx ON t(c)");
58705    }
58706
58707    #[test]
58708    fn test_alter_table_partition_hive() {
58709        // Hive: ALTER TABLE x PARTITION(y=z) ADD COLUMN a VARCHAR(10)
58710        assert_roundtrip("ALTER TABLE x PARTITION(y = z) ADD COLUMN a VARCHAR(10)");
58711    }
58712
58713    #[test]
58714    fn test_alter_table_change_column_hive() {
58715        // Hive/MySQL: CHANGE COLUMN old_name new_name data_type
58716        assert_roundtrip("ALTER TABLE x CHANGE COLUMN a a VARCHAR(10)");
58717    }
58718
58719    #[test]
58720    fn test_alter_table_add_columns_hive() {
58721        // Hive/Spark: ADD COLUMNS (col1 TYPE, col2 TYPE)
58722        assert_roundtrip("ALTER TABLE X ADD COLUMNS (y INT, z STRING)");
58723    }
58724
58725    #[test]
58726    fn test_alter_table_add_columns_cascade_hive() {
58727        // Hive/Spark: ADD COLUMNS (col1 TYPE, col2 TYPE) CASCADE
58728        assert_roundtrip("ALTER TABLE X ADD COLUMNS (y INT, z STRING) CASCADE");
58729    }
58730
58731    #[test]
58732    fn test_group_by_with_cube() {
58733        // Hive/MySQL: GROUP BY ... WITH CUBE
58734        let sql = "SELECT key, value FROM T1 GROUP BY key, value WITH CUBE";
58735        let result = Parser::parse_sql(sql).unwrap();
58736        let select = result[0].as_select().unwrap();
58737
58738        if let Some(group_by) = &select.group_by {
58739            // Debug: print the expressions
58740            eprintln!("GROUP BY expressions: {:?}", group_by.expressions);
58741
58742            // Check if there's a Cube expression with empty expressions
58743            let has_cube = group_by.expressions.iter().any(|e| {
58744                if let Expression::Cube(c) = e {
58745                    c.expressions.is_empty()
58746                } else {
58747                    false
58748                }
58749            });
58750            assert!(
58751                has_cube,
58752                "Should have a Cube expression with empty expressions in GROUP BY"
58753            );
58754        } else {
58755            panic!("Should have GROUP BY clause");
58756        }
58757    }
58758
58759    #[test]
58760    fn test_group_by_with_rollup() {
58761        // Hive/MySQL: GROUP BY ... WITH ROLLUP
58762        let sql = "SELECT key, value FROM T1 GROUP BY key, value WITH ROLLUP";
58763        let result = Parser::parse_sql(sql).unwrap();
58764        let select = result[0].as_select().unwrap();
58765
58766        if let Some(group_by) = &select.group_by {
58767            // Check if there's a Rollup expression with empty expressions
58768            let has_rollup = group_by.expressions.iter().any(|e| {
58769                if let Expression::Rollup(r) = e {
58770                    r.expressions.is_empty()
58771                } else {
58772                    false
58773                }
58774            });
58775            assert!(
58776                has_rollup,
58777                "Should have a Rollup expression with empty expressions in GROUP BY"
58778            );
58779        } else {
58780            panic!("Should have GROUP BY clause");
58781        }
58782    }
58783
58784    #[test]
58785    fn test_opendatasource_dot_access() {
58786        use crate::dialects::DialectType;
58787        use crate::transpile;
58788
58789        // OPENDATASOURCE(...).Catalog.dbo.Products — 3-part dot access
58790        let sql =
58791            "SELECT * FROM OPENDATASOURCE('SQLNCLI', 'Data Source=remote;').Catalog.dbo.Products";
58792        let result = transpile(sql, DialectType::TSQL, DialectType::TSQL).unwrap();
58793        assert_eq!(result[0], sql);
58794
58795        // 2-part dot access
58796        let sql2 = "SELECT * FROM OPENDATASOURCE('SQLNCLI', 'x').schema1.table1";
58797        let result2 = transpile(sql2, DialectType::TSQL, DialectType::TSQL).unwrap();
58798        assert_eq!(result2[0], sql2);
58799
58800        // 1-part dot access
58801        let sql3 = "SELECT * FROM OPENDATASOURCE('SQLNCLI', 'x').table1";
58802        let result3 = transpile(sql3, DialectType::TSQL, DialectType::TSQL).unwrap();
58803        assert_eq!(result3[0], sql3);
58804
58805        // No dot access (should still work as plain function)
58806        let sql4 = "SELECT * FROM OPENDATASOURCE('SQLNCLI', 'x')";
58807        let result4 = transpile(sql4, DialectType::TSQL, DialectType::TSQL).unwrap();
58808        assert_eq!(result4[0], sql4);
58809    }
58810
58811    #[test]
58812    fn test_exec_output_param() {
58813        use crate::dialects::DialectType;
58814        use crate::transpile;
58815
58816        // OUTPUT parameter
58817        let sql = "EXECUTE sp_CountOrders @region = 'US', @total = @count OUTPUT";
58818        let result = transpile(sql, DialectType::TSQL, DialectType::TSQL);
58819        assert!(
58820            result.is_ok(),
58821            "OUTPUT param should parse: {:?}",
58822            result.err()
58823        );
58824        assert_eq!(result.unwrap()[0], sql);
58825
58826        // WITH RESULT SETS (opaque — stored as Command)
58827        let sql2 = "EXEC sp_GetReport WITH RESULT SETS ((id INT, name NVARCHAR(100)))";
58828        let result2 = Parser::parse_sql(sql2);
58829        assert!(
58830            result2.is_ok(),
58831            "RESULT SETS should parse: {:?}",
58832            result2.err()
58833        );
58834
58835        // Dynamic SQL: EXECUTE (@sql)
58836        let sql3 = "EXECUTE (@sql)";
58837        let result3 = transpile(sql3, DialectType::TSQL, DialectType::TSQL);
58838        assert!(
58839            result3.is_ok(),
58840            "Dynamic SQL should parse: {:?}",
58841            result3.err()
58842        );
58843    }
58844}
58845
58846#[cfg(test)]
58847mod join_marker_tests {
58848    use super::*;
58849    use crate::dialects::DialectType;
58850
58851    #[test]
58852    fn test_oracle_join_marker_simple() {
58853        let sql = "select a.baz from a where a.baz = b.baz (+)";
58854        let result = Parser::parse_sql(sql);
58855        println!("Result: {:?}", result);
58856        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58857    }
58858
58859    #[test]
58860    fn test_oracle_join_marker_with_comma_join_and_aliases() {
58861        let sql = "SELECT e1.x, e2.x FROM e e1, e e2 WHERE e1.y = e2.y (+)";
58862        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58863        println!("Result: {:?}", result);
58864        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58865    }
58866
58867    #[test]
58868    fn test_oracle_xmltable_with_quoted_dot_columns() {
58869        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";
58870        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58871        println!("Result: {:?}", result);
58872        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58873    }
58874
58875    #[test]
58876    fn test_optimize_table_mysql() {
58877        use crate::dialects::DialectType;
58878        use crate::transpile;
58879
58880        // Multi-statement: TRUNCATE + OPTIMIZE
58881        let sql1 = "TRUNCATE TABLE session_logs";
58882        let r1 = transpile(sql1, DialectType::MySQL, DialectType::MySQL);
58883        assert!(r1.is_ok(), "TRUNCATE should parse: {:?}", r1.err());
58884
58885        let sql2 = "OPTIMIZE TABLE temp_exports";
58886        let r2 = transpile(sql2, DialectType::MySQL, DialectType::MySQL);
58887        assert!(r2.is_ok(), "OPTIMIZE should parse: {:?}", r2.err());
58888        assert_eq!(r2.unwrap()[0], sql2);
58889    }
58890
58891    #[test]
58892    fn test_mysql_index_hints() {
58893        use crate::dialects::DialectType;
58894        use crate::transpile;
58895
58896        // USE INDEX with alias
58897        let sql1 = "SELECT * FROM t e USE INDEX (idx1) WHERE a = 1";
58898        let r1 = transpile(sql1, DialectType::MySQL, DialectType::MySQL);
58899        assert!(r1.is_ok(), "USE INDEX with alias: {:?}", r1.err());
58900
58901        // IGNORE INDEX in JOIN with PRIMARY keyword
58902        let sql2 = "SELECT * FROM t1 JOIN t2 IGNORE INDEX (PRIMARY) ON t1.id = t2.id";
58903        let r2 = transpile(sql2, DialectType::MySQL, DialectType::MySQL);
58904        assert!(r2.is_ok(), "IGNORE INDEX PRIMARY: {:?}", r2.err());
58905
58906        // Full example from issue
58907        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";
58908        let r3 = transpile(sql3, DialectType::MySQL, DialectType::MySQL);
58909        assert!(r3.is_ok(), "Full example: {:?}", r3.err());
58910    }
58911
58912    #[test]
58913    fn test_oracle_quoted_dot_projection() {
58914        let sql = "SELECT warehouse2.\"Water\", warehouse2.\"Rail\" FROM warehouses warehouse2";
58915        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58916        println!("Result: {:?}", result);
58917        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58918    }
58919
58920    #[test]
58921    fn test_oracle_xmltable_columns_only() {
58922        let sql = "SELECT * FROM XMLTABLE('/Warehouse' PASSING warehouses.warehouse_spec COLUMNS \"Water\" varchar2(6) PATH 'WaterAccess', \"Rail\" varchar2(6) PATH 'RailAccess') warehouse2";
58923        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58924        println!("Result: {:?}", result);
58925        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58926    }
58927
58928    #[test]
58929    fn test_spark_limit() {
58930        use crate::dialects::DialectType;
58931        use crate::transpile;
58932
58933        // Spark LIMIT should work
58934        let sql = "SELECT * FROM something LIMIT 100";
58935        let r = transpile(sql, DialectType::Spark, DialectType::Spark);
58936        assert!(r.is_ok(), "Spark LIMIT: {:?}", r.err());
58937        assert_eq!(r.unwrap()[0], sql);
58938
58939        // Hive LIMIT should work
58940        let r2 = transpile(sql, DialectType::Hive, DialectType::Hive);
58941        assert!(r2.is_ok(), "Hive LIMIT: {:?}", r2.err());
58942    }
58943
58944    #[test]
58945    fn test_oracle_projection_alias_then_quoted_dot() {
58946        let sql =
58947            "SELECT warehouse_name warehouse, warehouse2.\"Water\" FROM warehouses warehouse2";
58948        let result = crate::dialects::Dialect::get(DialectType::Oracle).parse(sql);
58949        println!("Result: {:?}", result);
58950        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58951    }
58952}
58953
58954#[cfg(test)]
58955mod clickhouse_parser_regression_tests {
58956    use crate::dialects::DialectType;
58957
58958    #[test]
58959    fn test_clickhouse_select_format_clause_not_alias() {
58960        let sql = "SELECT 1 FORMAT TabSeparated";
58961        let result = crate::dialects::Dialect::get(DialectType::ClickHouse).parse(sql);
58962        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58963    }
58964
58965    #[test]
58966    fn test_clickhouse_projection_select_group_by_parses() {
58967        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()";
58968        let result = crate::dialects::Dialect::get(DialectType::ClickHouse).parse(sql);
58969        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58970    }
58971
58972    /// ClickHouse ternary operator AST structure tests.
58973    /// Ported from Python sqlglot: tests/dialects/test_clickhouse.py::test_ternary (lines 765-778).
58974    /// Verifies that `x ? (y ? 1 : 2) : 3` parses into nested IfFunc nodes
58975    /// with the correct AST shape.
58976    #[test]
58977    fn test_clickhouse_ternary_ast_structure() {
58978        use crate::expressions::Expression;
58979
58980        let result = crate::parse_one("x ? (y ? 1 : 2) : 3", DialectType::ClickHouse);
58981        assert!(result.is_ok(), "Parse error: {:?}", result.err());
58982        let ternary = result.unwrap();
58983
58984        // Root should be IfFunc
58985        let if_func = match &ternary {
58986            Expression::IfFunc(f) => f,
58987            other => panic!("Expected IfFunc, got {:?}", std::mem::discriminant(other)),
58988        };
58989
58990        // this (condition) should be Column "x"
58991        assert!(
58992            matches!(&if_func.condition, Expression::Column(_)),
58993            "Expected condition to be Column, got {:?}",
58994            std::mem::discriminant(&if_func.condition)
58995        );
58996
58997        // true branch should be Paren
58998        assert!(
58999            matches!(&if_func.true_value, Expression::Paren(_)),
59000            "Expected true_value to be Paren, got {:?}",
59001            std::mem::discriminant(&if_func.true_value)
59002        );
59003
59004        // false branch should be Literal
59005        let false_value = if_func.false_value.as_ref().expect("Expected false_value");
59006        assert!(
59007            matches!(false_value, Expression::Literal(_)),
59008            "Expected false_value to be Literal, got {:?}",
59009            std::mem::discriminant(false_value)
59010        );
59011
59012        // Inside the Paren, the nested ternary should also be IfFunc
59013        let inner_paren = match &if_func.true_value {
59014            Expression::Paren(p) => p,
59015            _ => unreachable!(),
59016        };
59017        let nested_if = match &inner_paren.this {
59018            Expression::IfFunc(f) => f,
59019            other => panic!(
59020                "Expected nested IfFunc, got {:?}",
59021                std::mem::discriminant(other)
59022            ),
59023        };
59024
59025        // Nested condition should be Column "y"
59026        assert!(
59027            matches!(&nested_if.condition, Expression::Column(_)),
59028            "Expected nested condition to be Column, got {:?}",
59029            std::mem::discriminant(&nested_if.condition)
59030        );
59031
59032        // Nested true should be Literal 1
59033        assert!(
59034            matches!(&nested_if.true_value, Expression::Literal(_)),
59035            "Expected nested true_value to be Literal, got {:?}",
59036            std::mem::discriminant(&nested_if.true_value)
59037        );
59038
59039        // Nested false should be Literal 2
59040        let nested_false = nested_if
59041            .false_value
59042            .as_ref()
59043            .expect("Expected nested false_value");
59044        assert!(
59045            matches!(nested_false, Expression::Literal(_)),
59046            "Expected nested false_value to be Literal, got {:?}",
59047            std::mem::discriminant(nested_false)
59048        );
59049    }
59050
59051    /// Verify that `a AND b ? 1 : 2` has And as the ternary condition
59052    /// (AND binds tighter than ?).
59053    /// Ported from Python sqlglot: test_clickhouse.py line 778.
59054    #[test]
59055    fn test_clickhouse_ternary_and_precedence() {
59056        use crate::expressions::Expression;
59057
59058        let result = crate::parse_one("a and b ? 1 : 2", DialectType::ClickHouse);
59059        assert!(result.is_ok(), "Parse error: {:?}", result.err());
59060        let ternary = result.unwrap();
59061
59062        let if_func = match &ternary {
59063            Expression::IfFunc(f) => f,
59064            other => panic!("Expected IfFunc, got {:?}", std::mem::discriminant(other)),
59065        };
59066
59067        // The condition should be And (not just Column "b")
59068        assert!(
59069            matches!(&if_func.condition, Expression::And(_)),
59070            "Expected condition to be And, got {:?}",
59071            std::mem::discriminant(&if_func.condition)
59072        );
59073    }
59074
59075    #[test]
59076    fn test_parse_interval_bare_number_duckdb() {
59077        use crate::dialects::{Dialect, DialectType};
59078        let sql = "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL 3 DAY";
59079        let d = Dialect::get(DialectType::DuckDB);
59080        match d.parse(sql) {
59081            Ok(result) => {
59082                assert!(!result.is_empty(), "Should parse to at least one statement");
59083                // Test transpilation to DuckDB target - should normalize number to quoted string
59084                let output_duckdb = d.transpile_to(sql, DialectType::DuckDB).unwrap();
59085                assert_eq!(
59086                    output_duckdb[0],
59087                    "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL '3' DAY",
59088                    "DuckDB output should have quoted interval value"
59089                );
59090                // Test transpilation to Hive target
59091                let output_hive = d.transpile_to(sql, DialectType::Hive).unwrap();
59092                assert_eq!(
59093                    output_hive[0], "SELECT CAST('2018-01-01 00:00:00' AS DATE) + INTERVAL '3' DAY",
59094                    "Hive output should have quoted interval value"
59095                );
59096            }
59097            Err(e) => panic!("Failed to parse DuckDB INTERVAL 3 DAY: {}", e),
59098        }
59099    }
59100}